INGENIERÍA DEL SOFTWARE. 4º ING. INFORMÁTICA (UPV/EHU) 1 de JUNIO de 2005 NOMBRE: GRUPO: 1) (0,25 ptos.) (Tiempo: 5 minutos) ¿Qué es robustez del software? Es un factor externo de calidad del software, que indica la capacidad del software para re accionar apropiadamente ante condiciones anormales, esto es, ante condiciones no contempladas en la especificación. 2) (0,25 ptos.) (Tiempo estimado: 5 minutos) ¿Qué son los servicios Web? Un servicio Web es lógica del negocio accesible mediante protocolos estándar en Internet: habitualmente HTTP para el transporte, SOAP (basado en XML) para la invocación, WSDL para la definición del servicio. 3) (1 pto.) (Tiempo estimado: 20 minutos) Dado el caso de uso PEDIR BILLETE, y el siguiente caso de prueba Entrada: nombre “Kepa Sola” Condiciones de entrada: Existen 25 billetes libres y 5 ocupados en la tabla BILLETES(NUMERO,NOMBRE,ESTADO) Salida: dar número de billete x (x>0), quedando 24 billetes libres, 6 ocupados y el billete con el número x está ocupado en la BD. Escribir un componente de prueba para poder probar al menos el caso de prueba anterior, teniendo en cuenta que disponemos de las siguientes clases. NOTA: Si se usa alguna otra clase auxiliar, describirla usando UML. PedirBillete -logNeg : GestorBilletes «interface» GestorBilletes +getBillete(in nombre : string) : int «subsystem» java.rmi ServidorGestorBilletesBD +getBillete(in nombre : string) : int «subsystem» java.sql SOLUCIÓN: public class ComponentePruebaEntrSistema { GestorBilletes ln; OperacionesParaPruebas lp = new OperacionesParaPruebas(); public static void main(String[] args) { try{ ln = java.rmi.Naming.lookup("rmi://MAQ:PUERTO/gestorBilletes"); lp.inicializarBilletes(25,5); // 25 libres, 5 ocupados int i = ln.getBillete("Kepa Sola"); // Llamar a lógica negocio int quedanLibres = lp.getNumLibres(); int quedanOcupados = lp.getNumOcupados(); boolean esCorrecto = lp.existeBillete(i,"Kepa Sola","OCUPADO"); if ((i>0)&&(quedanLibres==24)&& (quedanOcupados==6)&&(esCorrecto)) System.out.println("CP1 correcto"); else System.out.println("CP1 incorrecto"); } catch (Exception e) {e.printStackTrace();} } donde se usa la clase: 4) (3 ptos.) (Tiempo estimado: 60 minutos) La siguiente clase (ConsultarAutobuses.java) implementa el caso de uso CONSULTAR HORARIOS AUTOBUSES, que permite escoger una ciudad de salida, otra de llegada y consultar los horarios de autobuses entre ellas. Los puntos de salida, llegada y horarios se obtienen de las siguientes tablas: Se pide : 1) (1,5 ptos.) Proponer una solución que utilice una arquitectura física en 3 niveles. Se deben mostrar las clases necesarias en notación UML, junto con la signatura de los métodos de las mismas. No hay que implementar completamente la solución, pero sí mostrar el uso de Java RMI, Java JDBC e interfaces Java. Esto se puede representar por medio de dependencias “usa”, generalizaciones e implementación de interfaces en UML. También se puede implementar algún método de alguna clase si eso ayuda a explicar mejor la solución. Suponiendo que la solución propuesta está completamente implementada, hay que analizar ahora qué habría que cambiar para satisfacer una serie de nuevos requisitos. Para cada nuevo requisito hay que indicar claramente si se cree que afecta al nivel de presentación, lógica del negocio o nivel de datos, explicar los cambios que habría que hacer en el diseño y/o implementación e indicar si la solución era extensible ante cada cambio. 2) (0,5 ptos.) Se desea cambiar de SGBD: MySQL en vez de Access. 3) (0,5 ptos.) Queremos que al escoger una ciudad de salida en la lista desplegable correspondiente, sólo aparezcan en la otra aquellas ciudades a las que llega un autobús desde dicha ciudad. Lo mismo cuando se escoja una ciudad de llegada. 4) (0,5 ptos.) Se desea que aparezcan todas las ciudades y pueblos en los que para el autobús y desde los que se puede coger el autobús. El código es el siguiente: package autobuses; import ... public class ConsultarAutobuses extends JFrame { private JPanel jPanel1 = new JPanel(); private BorderLayout borderLayout1 = new BorderLayout(); private JLabel jLabel1 = new JLabel(); private DefaultComboBoxModel model1 = new DefaultComboBoxModel(); private JComboBox jComboBox1 = new JComboBox(model1); private JLabel jLabel2 = new JLabel(); private DefaultComboBoxModel model2 = new DefaultComboBoxModel(); private JComboBox jComboBox2 = new JComboBox(model2); private JButton jButton1 = new JButton(); private DefaultListModel model3 = new DefaultListModel(); private JList jList1 = new JList(model3); private Statement st; private ResultSet rs; private static Connection conexion; public ConsultarAutobuses() { try { jbInit(); } catch(Exception e) { e.printStackTrace();} } private void jbInit() throws Exception { Class.forName("sun.jdbc.odbc.JdbcOdbcDriver"); conexion = DriverManager.getConnection("jdbc:odbc:bdautobus"); st = conexion.createStatement(); this.getContentPane().setLayout(borderLayout1); this.setSize(new Dimension(400, 300)); this.setTitle("Consultar Horarios Autobuses"); jLabel1.setText("Salida:"); jLabel2.setText("Llegada:"); jButton1.setText("Consultar Horarios"); jButton1.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) {jButton1_actionPerformed(e); }}); jPanel1.add(jLabel1, null); jPanel1.add(jComboBox1, null); jPanel1.add(jLabel2, null); jPanel1.add(jComboBox2, null); rellenarModel(model1,obtenerElementos("SELECT SALIDA FROM AUTOBUS")); rellenarModel(model2,obtenerElementos("SELECT LLEGADA FROM AUTOBUS")); this.getContentPane().add(jPanel1, BorderLayout.NORTH); this.getContentPane().add(jButton1, BorderLayout.SOUTH); this.getContentPane().add(jList1, BorderLayout.CENTER); } public void rellenarModel(DefaultComboBoxModel d,Vector elems) { d.removeAllElements(); for (int i=0;i<elems.size();i++) { d.addElement(elems.elementAt(i)); } } public void rellenarModel(DefaultListModel d,Vector elems) { d.removeAllElements(); for (int i=0;i<elems.size();i++) { d.addElement(elems.elementAt(i)); } } public Vector obtenerElementos(String sql) { Vector v = new Vector(); try{ rs = st.executeQuery(sql); while (rs.next()) {v.addElement(rs.getString(1)); } } catch(Exception e) {System.out.println("Error: "+e.toString());} return v; } private void jButton1_actionPerformed(ActionEvent e) { rellenarModel(model3, obtenerElementos("SELECT horario "+ "FROM horarios INNER JOIN Autobus ON horarios.codigo=Autobus.codigo "+ "WHERE SALIDA='"+jComboBox1.getSelectedItem()+ "' AND LLEGADA='"+jComboBox2.getSelectedItem()+"'")); }} SOLUCIÓN 1): NOTA: en la solución no se esperaba tanto detalles como los que se dan aquí. Eso sí, los nombres de las clases e interfaces y los métodos de la interfaz con la lógica del negocio sí. En particular, los nombres de los atributos de las clases no son necesarios. SOLUCIÓN 2): En la lógica del negocio hay que cargar un driver de MySQL (por ejemplo el org.gjt.mm.mysql.Driver) y la base de datos de autobuses. Esto se puede hacer, por ejemplo, en el constructor de ServidorGestorAutobuses Class.forName("org.gjt.mm.mysql.Driver"); conexion=DriverManager.getConnection("jdbc:mysql://localhost/BDAutobuses"); Hay que cambiar el nivel de datos: instalar el SGBD MySQL y crear la BD BDAutobuses. El nivel de presentación no se ve afectado. Podría ser que la lógica del negocio no tuviera que cambiarse si el driver del SGBD y el string de conexión se hubieran leído de un fichero de configuración. En este caso, sí podríamos haber considerado la solución como muy extensible, ya que un cambio que en principio afecta sólo al nivel de datos (cambio en el SGBD y la BD), se ha realizado sólo modificando dicho nivel de datos (y además instalando los drivers de MySQL en los servidores de aplicaciones) SOLUCIÓN 3): En la lógica del negocio hay que añadir algún método: getLlegadas(salida: String): Vector getSalidas(llegada:String): Vector que se implementarían devolviendo los resultados a estas preguntas, respectivamente: SELECT LLEGADA FROM AUTOBUS WHERE SALIDA = %salida% SELECT SALIDA FROM AUTOBUS WHERE LLEGADA = %llegada% El nivel de presentación debe cambiarse para que al escoger una ciudad en una lista desplegable (JComboBox) se rellene la otra lista desplegable: rellenarModel(modX,logNeg.getLlegadas(jCombX.getSelectedItem().toString()) El nivel de datos no se ve afectado. En principio, se puede considerar que la solución era extensible ya que un cambio que afecta al nivel de presentación (qué debe aparecer en una lista desplegable de la interfaz gráfica) afecta al nivel de presentación, y al nivel de lógica del negocio porque esa operación no estaba considerada (en cierta manera es un nuevo requisito). SOLUCIÓN 4): En este caso no se indica claramente qué es lo que debe aparecer en la interfaz gráfica (en la presentación) por lo que la siguiente solución sería válida: Añadir una tupla en la tabla AUTOBUS por cada posible trayecto, y tantas por cada horario. Por ejemplo: <x,Lasarte,Andoain> y <x,8.40> y <x,9.40> Esta solución no implica un cambio en la aplicación. Sólo hay que añadir las tuplas correspondientes en la base de datos. Nota: En realidad, seguramente no será una solución correcta porque hemos puesto un código de autobús por cada trayecto, y, claro está, no sale un nuevo autobús desde cada parada a todas las siguientes. Si se quisiera que en la interfaz gráfica aparecieran todas las paradas junto con sus horarios, entonces se necesitarían cambios en todos los niveles: en la BD habría que añadir datos sobre las paradas y sus horarios. En la lógica del negocio habría que añadir métodos para obtener esos datos. Y en la presentación habría que decidir en qué parte de la interfaz gráfica se muestra dicha información y hacerlo. 5) (2 ptos.) (Tiempo estimado: 40 minutos) Se desea realizar una aplicación informática para gestionar las competiciones de natación. Una competición está compuesta por Nadadores y Pruebas, donde un nadador que compite en una prueba obtiene un resultado. El resultado además del tiempo obtenido (min, seg, cent) tiene dos asociaciones que refe rencian al nadador que lo ha realizado, y la prueba obtenida. En el diagrama UML se reflejan todas las clases y asociaciones que existen en el modelo. Utilizando los algoritmos y predicados JGL, se pide implementar los siguientes métodos en la clase Competicion: 1- Visualizar ordenados por tiempo, los nadadores que han participado en una prueba pru. void nadadoresPrueba(String pru) 2- Visualizar los nadadores que hayan competido en más de n pruebas. void nadadoresMasDeNPruebas(int n) 3- Visualizar los nadadores que han realizado un tiempo inferior a m minutos, s segundos y c centésimas en la prueba pru void nadadoresTiempoMenor(String pru, int m, int s, int c) Solución 1) void nadadoresPrueba(String pru){ Prueba p=(Prueba)pruebas.get(pru); BinaryPredicate bp=new BinaryComposePredicate(new LessNumber(), new ObtenCentesimas(),new ObtenCentesimas()); Sorting.sort(p.resultados,bp); Applying.forEach(p.resultados,new PrintResultadoNadador()); } Solución 2) void nadadoresMasDeNPruebas(int n){ UnaryPredicate up=new BindSecondPredicate(new GreaterNumber(), new Integer(n)); UnaryPredicate up2=new UnaryComposePredicate(up,new ObtenerPruebas()); Container c=Filtering.select(nadadores,up2); Applying.forEach(c,new PrintNadador()); } Solución 3) void nadadoresTiempoMenor(String pru, int m, int s, int c){ double t=m*6000+s*100+c; Prueba p=(Prueba)pruebas.get(pru); UnaryPredicate up=new BindSecondPredicate(new LessNumber(),new Double(t)); UnaryPredicate up2=new UnaryComposePredicate(up,new ObtenCentesimas()); Container cont=Filtering.select(p.resultados,up2); Applying.forEach(cont,new PrintResultadoNadador()); } Funciones auxiliares public class ObtenCentesimas implements UnaryFunction { public Object execute(Object o) { Resultado res=(Resultado)o; return new Double(res.min*6000+res.seg*100+res.cent); } } public class PrintResultadoNadador implements UnaryFunction { public Object execute(Object o) { Resultado res=(Resultado)o; System.out.println(res.nad.nombre+" "+res.nad.club+" :"+res.min+"'"+res.seg+"''"+res.cent); return null; } } public class PrintNadador implements UnaryFunction { public Object execute(Object o) { Pair p=(Pair)o; Nadador nad=(Nadador)p.second; System.out.println(nad.nombre+" "+nad.club); return null; } } public class Obtene rPruebas implements UnaryFunction { public Object execute(Object o) { Pair p=(Pair)o; Nadador nad=(Nadador)p.second; return(new Integer(nad.resultados.size())); } } 6) (2 ptos.) (Tiempo estimado: 30 minutos) Se desea realizar una aplicación para la evaluación de expresiones aritméticas. Una expresión aritmética es: a) Un número, o bien b) Un operador binario que opera sobre dos expresiones aritméticas. El único requisito del sistema es: 1- Los operadores para los que se defi ne el sistema son inicialmente la Suma, Resta, Producto y División, pero no se descarta que en el futuro se pueda extender a otras operaciones como Exponencial o Logaritmo. Realiza la aplicación teniendo en cuenta este requisito. Se pide: 1. Realizar un modelo que permita recoger expresiones aritméticas 2. Implementa las clases del modelo 3. Realiza un programa que devuelva el resultado de 4+5 * 6-2 (Es decir, 36). SOLUCIÓN: public class Compuesta implements Expresion { Operador op; Expresion exp1, exp2; public Compuesta(Operador o, Expresion e1, Expresion e2) { op=o; exp1=e1; exp2=e2; } public float getValor() { return op.evalua(exp1.getValor(),exp2.getValor()); } } public interface Operador { public float evalua(float x, float y); } public class Suma implements Operador { public float evalua(float p1, float p2){ return p1+p2; } } Programa principal: public static void main(String[] args) { Expresion n4=new Numero(4); Expresion n5=new Numero(5); Expresion n6=new Numero(6); Expresion n2=new Numero(2); Expresion s1=new Compuesta(new Suma(),n4,n5); Expresion s2=new Compuesta(new Resta(),n6,n2); Expresion s3=new Compuesta(new Producto(),s1,s2); System.out.println(s3.getValor()); } 7) (1 .5 ptos.) (Tiempo estimado: 20 minutos) Queremos realizar un componente que nos sirva como Monedero Virtual a la hora de realizar compras en Internet. El monedero cuando se crea se inicializa con una cantidad q, y las operaciones que están disponibles son: 1- void añadirCantidad(int q). Se incrementa el monedero con una cantidad q. 2- boolean realizarCompra(String articulo, int precio). Devuelve cierto si hay saldo suficiente, falso en caso contrario. 3- Container articulosComprados(). Devuelve en un contenedor los artículos que se han comprado así como su precio de compra. Se pide: 1. Realizar un componente que implemente este Monedero Virtual. NOTA: Las interfaces javax.ejb.EJBHomeObject extienden de las clases: javax.ejb.EJBObject SOLUCIÓN: Interface EJB public interface MonederoEJB extends EJBObject { void añadirCantidad(int q) throws RemoteException; boolean realizarCompra(String articulo, int precio) throws RemoteException; Contain er articulosComprador() throws RemoteException; } Interface Home public interface MonederoEJBHome extends EJBHome { MonederoEJB create(int q) throws RemoteException, CreateException; } Componente public class MonederoEJBBean implements SessionBean { SList articulos=new SList(); int cantidad; private SessionContext context; public void ejbCreate(int q) { cantidad=q; } public void añadirCantidad(int q) y { cantidad=cantidad+q; } public boolean realizarCompra(String articulo, int precio) { if (cantidad>precio) { articulos.add(new Articulo(articulo, precio)); cantidad=cantidad-precio; return true; } else return false; } public Container articulosComprador() { return articulos; } }