Download 28 de MAYO de 2007

Document related concepts
no text concepts found
Transcript
INGENIERÍA DEL SOFTWARE. 4º ING. INFORMÁTICA (UPV/EHU)
28 de MAYO de 2007
NOMBRE:
GRUPO:
1ª PREGUNTA: ARQUITECTURAS EN 3 NIVELES) (2.5 ptos.) (Tiempo estimado: 45 minutos)
A continuación se presenta la implementación usando una arquitectura en 3 niveles físicos del caso
de uso PEDIR BILLETE que se ha utilizado durante este cuatrimestre (es exactamente el que
aparecía en la página web de la asignatura).
package ejsA3N;
import java.rmi.*;
public interface GestorBilletes extends Remote
{ public Billete getBillete(String nom) throws RemoteException; }
package ejsA3N;
import java.util.Date;
public class Billete implements java.io.Serializable
{ private int num; private String nomb; private Date fecha;
public Billete(int n,String nom){num=n; nomb=nom; fecha=new Date(); }
public int getNum(){return num;}
public String getNombre(){return nombre;}
public Date getFecha(){return fecha;} }
package ejsA3N;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.rmi.*;
public class PedirBillete extends JFrame {
JPanel jPanel1 = new JPanel(); JLabel jLabel1 = new JLabel();
JTextField jTextField1 = new JTextField();
JButton jButton1 = new JButton();
JTextArea jTextArea1 = new JTextArea();
GestorBilletes gestorBilletes; // Objeto con la lógica del negocio
public PedirBillete() {
super();
try {jbInit();} catch (Exception e) {e.printStackTrace();}}
private void jbInit() throws Exception {
this.getContentPane().setLayout(null);
this.setSize(new Dimension(400, 300));
jPanel1.setLayout(null);
jPanel1.setBounds(new Rectangle(0, 0, 392, 273));
jLabel1.setText("Nombre:");
jLabel1.setBounds(new Rectangle(54, 58, 64, 17));
jTextField1.setBounds(new Rectangle(124, 50, 163, 33));
jButton1.setText("Pedir Billete");
jButton1.setBounds(new Rectangle(103, 189, 156, 44));
jButton1.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(ActionEvent e) {
jButton1_actionPerformed(e);} });
jTextArea1.setBounds(new Rectangle(88, 94, 189, 76));
this.setTitle("Pedir Billetes");
this.getContentPane().add(jPanel1, null);
jPanel1.add(jLabel1, null); jPanel1.add(jTextField1, null);
jPanel1.add(jButton1, null); jPanel1.add(jTextArea1, null); }
public void setGestorBilletes(GestorBilletes g) { gestorBilletes=g; }
void jButton1_actionPerformed(ActionEvent e) {
try{int res =
gestorBilletes.getBillete(jTextField1.getText()).getNum();
if (res<0) jTextArea1.append("Error al asignar billete");
else jTextArea1.append("Asignado. \nReferencia: "+res+"\n");
} catch (Exception er) {System.out.println("Error: "+er.toString());
jTextArea1.append("Error al asignar billete");}}
public static void main (String []arg) {
PedirBillete b = new PedirBillete();
System.setSecurityManager(new RMISecurityManager());
String servicioRemoto = "rmi://localhost:1099/gestorBilletes”;
GestorBilletes objetoRemoto;
try{objetoRemoto = (GestorBilletes)Naming.lookup(servicioRemoto);
b.setGestorBilletes(objetoRemoto); }
catch (Exception e) {System.out.println("Error: "+e.toString());}
b.setVisible(true); }}
package ejsA3N;
import java.rmi.*;
import java.sql.*;
import java.io.*;
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:Billetes");
conexion.setAutoCommit(false);
sentencia=conexion.createStatement(); }
catch(Exception e){System.out.println("Error:"+e.toString());}}
public Billete getBillete(String nom)
throws RemoteException {
// Devuelve billete con nº billete, -1 si no hay, -2 problemas
String pregSQL = "SELECT NUMERO FROM BILLETES"+
" WHERE ESTADO=\'LIBRE\'";
try{ ResultSet rs = sentencia.executeQuery(pregSQL);
if (rs.next()) {
String num = rs.getString("NUMERO");
int act = sentencia.executeUpdate("UPDATE BILLETES"+
" SET ESTADO='OCUPADO', NOMBRE = '"+nom+
"' WHERE NUMERO="+num+" AND ESTADO='LIBRE'");
conexion.commit();
int n= Integer.parseInt(num);
if (act>0) return new Billete(n,nom); // N. bill asignado
return new Billete(-2,""); } // Otro ha OCUPADO billete
else return new Billete(-1,"");; } // No había libre
catch (SQLException e){System.out.println(e.toString());}
return new Billete(-2,""); } // Que prueben otra vez a llamar
public static void main(String[] args) {
System.setSecurityManager(new RMISecurityManager());
try { java.rmi.registry.LocateRegistry.createRegistry(numPuerto);
} catch (Exception e) {System.out.println(Rmiregistry exist");}
try { ServidorGestorBilletesBD objetoServidor =
new ServidorGestorBilletesBD();
String servicio="//localhost:1099/gestorBilletes";
Naming.rebind(servicio,objetoServidor);
} catch (Exception e){System.out.println(e.toString());}}}
Se trata de desarrollar un caso de uso similar al anterior llamado “PEDIR VARIOS BILLETES”
donde se puede pedir varios billetes a nombre de una persona. NOTA IMPORTANTE: si no se
pueden conseguir todos los billetes, entonces no se consigue ninguno: o todos o ninguno.
Se pide:
1) Diseñar en UML una solución para el caso de uso “PEDIR VARIOS BILLETES” que
utilice una arquitectura física en 3 niveles. Dicha solución debe ser extensible ante el
siguiente cambio en los requisitos: “se podrá pedir un número diferente de billetes a 1, 2 ó
3”. Por extensible se entenderá que “no habrá que modificar el nivel de presentación” en el
momento en que se decida aplicar el nuevo requisito.
2) Indicar TODOS LOS CAMBIOS que hay que hacer en la IMPLEMENTACIÓN con
respecto a la solución anterior del caso de uso “PEDIR BILLETES”. Cuando se trate de
un método nuevo hay que hacer la implementación completa del método.
Como ayuda incluimos parte de la implementación de la clase de presentación PedirNBilletes,
donde aparece solamente lo que es nuevo con respecto a la clase de presentación PedirBillete.
Nótese que en esta implementación sólo se permite pedir 1, 2 ó 3 billetes. Tampoco aparece la
implementación del evento “pulsar botón”, que claro está, se pide implementar.
...
public class PedirNBilletes extends JFrame {
...
JLabel jLabel2 = new JLabel();
JComboBox jComboBox1 = new JComboBox();
DefaultComboBoxModel comboBoxModel1 = new DefaultComboBoxModel();
...
private void jbInit() throws Exception {
...
jLabel2.setText("Num. billetes:");
jLabel2.setBounds(new Rectangle(55, 75, 115, 20));
jPanel1.add(jComboBox1, null);
jComboBox1.setBounds(new Rectangle(160, 75, 60, 20));
jComboBox1.setModel(comboBoxModel1);
this.setTitle("Pedir Varios Billetes");
comboBoxModel1.addElement("1");
comboBoxModel1.addElement("2");
comboBoxModel1.addElement("3"); }
void jButton1_actionPerformed(ActionEvent e) {// HAY QUE IMPLEMENTAR ESTO }
public static void main (String []arg) {...}
SOLUCIÓN:
Cambios en PedirNBilletes:
private void jbInit() throws Exception {
// ...
rellenarComboBox();
}
public void rellenarComboBox()
{
try{
for (int i=1;i<=gestorBilletes.getNumMaxBilletes();i++)
comboBoxModel1.addElement(String.valueOf(i));
} catch (Exception e) {System.out.println(e.toString());}
}
void jButton1_actionPerformed(ActionEvent e) {
try{
int numBilletes = Integer.parseInt(comboBoxModel1.getSelectedItem().toString());
Vector v = gestorBilletes.getBillete(numBilletes,jTextField1.getText());
if (v==null) jTextArea1.append("No hay billetes");
else
if (numBilletes!=v.size())
jTextArea1.append("No hay "+numBilletes+" billetes");
else
for (int i=0; i<v.size(); i++)
jTextArea1.append("Asignado: "+((Billete)v.elementAt(i)).getNum()+"\n");
} catch (Exception er) {
jTextArea1.append("\nError al asignar billete"+er.toString());}}
Cambios en GestorBilletes: añadir los nuevos métodos remotos.
public interface GestorBilletes extends Remote
{ //...
public Vector getBillete(int n, String nom) throws RemoteException;
public int getNumMaxBilletes() throws RemoteException;}
Cambios en ServidorGestorBilletesBD: añadir los nuevos métodos remotos.
public class ServidorGestorBilletesBD
//...
public static final int numMaxBilletes=3;
public Vector getBillete(int n, String nom)
throws RemoteException {
// Devuelve null si no hay billetes
}
String pregSQL = "SELECT NUMERO FROM BILLETES WHERE ESTADO=\'LIBRE\'";
int faltan=n;
Vector billetes = new Vector();
try{
ResultSet rs = sentencia.executeQuery(pregSQL);
boolean haEntrado=false;
while (rs.next() && (faltan>0)) {
haEntrado=true;
String num = rs.getString("NUMERO");
int act = sentencia2.executeUpdate("UPDATE BILLETES"+
" SET ESTADO='OCUPADO', NOMBRE = '"+nom+
"' WHERE NUMERO="+num+" AND ESTADO='LIBRE'");
if (act>0) {
int numero= Integer.parseInt(num);
billetes.addElement(new Billete(numero,nom));
faltan--;
}
}
if (faltan==0) { conexion.commit(); return billetes;}
else { conexion.rollback();
if (haEntrado) return new Vector();
else return null;
}
}
catch (SQLException e)
{System.out.println("Error: "+e.toString());}
return new Vector(); // Que prueben otra vez a llamar
public int getNumMaxBilletes() { return numMaxBilletes;}
2ª PREGUNTA: PRUEBAS) (1,5 ptos.) (Tiempo estimado: 15 minutos) Diseñar el siguiente caso de
prueba para el caso de uso anterior e implementar un componente de prueba en JUNIT que lo
pruebe.
// OBJETIVO DEL CASO DE PRUEBA: comprobar que “si se pide un número de billetes mayor
a los disponibles en un momento dado, entonces no se obtiene ningún billete, incluso aunque
por lo menos hubiera uno disponible”.
Entrada:
Condiciones de ejecución:
Resultado esperado
NOTA: Si para el componente de prueba se usa alguna otra clase auxiliar, describirla usando
UML.
import junit.framework.*;
public class ComponentePruebaPedirNBilletes extends TestCase
{
public ComponentePruebaPedirNBilletes (String nombre)
{ super(nombre);
}
protected void setUp() {
}
protected void tearDown() {
}
// assertTrue(b) falla si y solo si b es false
public static Test suite() {
return new TestSuite(ComponentePruebaPedirNBilletes.class);
}
public static void main(String args[]) {
junit.textui.TestRunner.run(suite());
}
}
SOLUCIÓN:
// OBJETIVO DEL CASO DE PRUEBA: comprobar que “si se pide un número de billetes mayor
a los disponibles en un momento dado, entonces no se obtiene ningún billete, incluso aunque
por lo menos hubiera uno disponible”.
Entrada: Solicitar 8 billetes a nombre de “NOHAYSUFICIENTES”
Condiciones de ejecución: En la tabla BILLETES(NUMERO,ESTADO,NOMBRE) de la BD
hay únicamente 6 billetes con ESTADO=’LIBRE’ y 8 billetes con ESTADO=’OCUPADO’
Resultado esperado: No se obtiene ningún billete, y además siguen existiendo los 6
billetes anteriores con ESTADO=’LIBRE’
public class ComponentePruebaPedirNBilletes extends TestCase
{
GestorBilletes logNeg;
OpsPruebas logPru;
public ComponentePruebaPedirNBilletes (String nombre)
{ super(nombre);
try{
logNeg = (GestorBilletes)Naming.lookup("rmi://localhost:1099/gestorBilletes");
logPru = new OpsPruebas();}
catch (Exception e) {System.out.println("Error: "+e.toString());} }}
protected void setUp() { }
protected void tearDown() { }
public void testPedirMasBilletesQueDisponibles() {
try{
logPru.inicializarBilletes(6,8); // Crea 6 libres y 8 ocupados
Vector v = logNeg.getBillete(8,"NOHAYSUFICIENTES");
int n = logPru.getNumBilletesLibres();
assertTrue((v.size()==0) && (n==6));
}
catch (Exception e) {System.out.println("Error al llamar "+e.toString());
assertTrue(false);}
}
public static Test suite() {
return new TestSuite(ComponentePruebaPedirNBilletes.class); }
public static void main(String args[]) {
junit.textui.TestRunner.run(suite()); }}
3ª PREGUNTA: JGL) (2 ptos.) (Tiempo estimado: 40 minutos) La clase función unaria de JGL
Prime devuelve como resultado si un número es primo. Basándote en esta clase, implementa la
función PrimoPositivoMasCercano que dado un número, devuelve su número primo positivo
más cercano.
2. La clase función UnaryCompose en JGL es una clase que tiene como atributos 2 objetos
UnaryFunction uf1 y uf2, y para un elemento x de entrada devuelve como resultado uf1(uf2(x)).
¿Cómo implementarías esta clase?
3. Realizar un programa que dado un Container de elementos enteros, devuelva en otro Container,
el resultado de transformar cada elemento del contenedor inicial por su primo positivo más
cercano, utilizando las clases definidas en los apartados 1 y 2.
Ejemplo: (-6,4,3,-9) > (7,5,3,11)
Nota:
El
algoritmo
Trasforming.transform(Container
c1<T>,
Container
c2<R>,
UnaryFunction<T,R> uf), aplica la funcion uf a cada elementos del container c1, dejando el
resultado en el Container c2.
SOLUCIÓN:
1.class PrimoPositivoMasCercano<T extends Number> implements UnaryFunction<T,T> {
public Integer execute (T obj) {
UnaryPredicate<T> esPrimo=new Prime<T>();
int dif=0;
int num;
if (obj.intValue()<0) num=-1* obj.intValue(); else num= obj.intValue();
while((!esPrimo.execute(num+dif) ||
(!esPrimo.execute(num-dif))
dif++;
if (esPrimo.execute(num+dif))
return new Integer(num+dif);
else
}
return new Integer(num-dif);
}
2.class UnaryCompose<T,U> implements UnaryFunction<T,U> {
UnaryFunction<T,R> uf1;
UnaryFunction<R,U> uf2;
U execute(T obj) {
return uf1.execute(uf2.execute(obj));
}
3. main() {
Container<Integer> src=new SList<Integer>();
Container<Integer> dst= new SList<Integer>();
UnaryFunction<Integer, Integer> primoMasCercano=new PrimoPositivoMasCercano
<Integer>();
Transforming.transform(src, dst, primoMasCercano);
}
4ª PREGUNTA: EJB) (2 ptos.) (Tiempo estimado: 30 minutos) Una Liga de Fútbol, para la
gestión de los partidos de una jornada ha desarrollado un componente que permite, crear partidos,
anotar un gol por parte de un equipo, consultar el marcador, comprobar si el partido está en juego
o no, y finalmente dar por terminado un partido. Cuando finaliza el partido, el componente
notifica al componente Gestor de Quinielas la información del partido jugado, es decir, los equipos
y el resultado (1X2).
Partido
GestorQuinielas
void anotarResultado (String eq1,
String eq2, String resultado)
Se pide implementar el componente Partido, que apoyándose en el componente GestorQuinielas,
permita:
1- Crear objetos Partido
2. Ejecutar las operaciones descritas en el párrafo anterior.
3. De qué tipo son los componentes Partido y GestorQuinielas?. Justifica brevemente tu respuesta
SOLUCIÓN:
public interface PartidoHome extends EJBHome {
Partido create(String eq1, String eq2) throws RemoteException,
CreateException; }
public interface Partido extends EJBObject {
boolean getEnJuego() throws RemoteException;
int getGolesFavor() throws RemoteException;
int getGolesContra() throws RemoteException;
void gol(String equipo) throws RemoteException;
void fin() throws RemoteException; }
public class PartidoBean implements SessionBean {
public String equipo1;
public String equipo2;
public boolean enJuego;
public int golesFavor;
public int golesContra;
public void ejbCreate(String eq1, String eq2) {
equipo1=eq1;
equipo2=eq2;
enJuego=true; }
public int getGolesFavor() {
return golesFavor;
}
public int getGolesContra() {
return golesContra;
}
public void gol(String equipo) {
if(equipo.compareTo(equipo1)==0)
golesFavor++;
if(equipo.compareTo(equipo2)==0)
golesContra++;
}
public void fin() {
Context context = getInitialContext();
QuinielasHome quinielasHome = (QuinielasHome) PortableRemoteObject.narrow(
context.lookup( "Quinielas" ), QuinielasHome.class );
GestorQuinielas gestorQuinielas;
gestorQuinielas = quinielasHome.create( );
String res;
if (golesFavor>golesContra) res="1";
else if (golesFavor<golesContra) res="2";
else if (golesFavor==golesContra) res="X";
gestorQuinielas.anotarResultado(equipo1, equipo2, res)
}
}
5ª PREGUNTA: PATRONES DE DISEÑO) (2 ptos.) (Tiempo estimado: 50 minutos) Queremos
construir una aplicación que gestione los goles que se vayan produciendo en una jornada de un
campeonato de fútbol. Una primera aproximación al modelo de dominio a utilizar para
implementar la aplicación será el reflejado en el Diagrama de Clases que se adjunta:
Partido
Jornada
1
*
MedioComunicacion
-identificador : String
-idEquipo1 : String
-idEquipo2 : String
+gol()
1
goles
*
PrensaDigital
Gol
-idEquipo : String
-idJugador : String
-minuto : String
+actualizarTabla()
Radio
+cantarGol()
Television
+actualizarTXT()
Siempre que se produzca un gol en la jornada el objeto Partido recibe un método gol() con la
siguiente información:
gol(String idEquipo, String idJugador, String minuto)
Si un medio de comunicación(PrensaDigital, Radio o Televisión) quiere recibir información
actualizada de los goles que se produzcan en la jornada debe suscribirse al partido o partidos de los
cuales quiere recibir información. La aplicación informará puntualmente a cada medio suscrito de
los goles que se produzcan en el campeonato. La aplicación deberá permitir que un medio de
comunicación pueda suscribirse invocando un solo método a todos los partidos de la jornada.
Se pide:
Diseñar un modelo de clases que permita recoger las funcionalidades descritas, indicando
claramente cuál(es) son los patrón(es) utilizados.
Implementar los las clases que estimes oportunas.
SOLUCIÓN:
Diseño
Implementación
public Class Jornada {
Vector<Partido> partidos;
public void subscribirse(MédioComunicacion mc){
Enumeration<Partido> e=partidos.elements();
Partido p;
while (e.hasMoreElements()) {
p=e.nextElement();
p.addObserver(mc); } } }
public class Partido extends Observable{
Gol ultimoGol;
public void gol() {
// Añadir el gol y guardarlo en ultimoGol
//Notificar a los médios interesados
setChanged();
notifyObservers(); }}
public class Prensa implements Observer
public void update(Observable t){
Partido p =(Partido)t;
Gol g=p.getUltimoGol();
actualizarTabla(g);
}
}