Inversion of Control ==================== 1) I would like to implement a server program that reports the grades of the students. That would be a program like the one I have in the webpage. 2) What is a student? public class Student implements Serializable { private static final long serialVersionUID = 1L; public Student(long key, String name, double grade) { this.name = name; this.grade = grade; this.key = key; } public final String name; public final double grade; public final long key; public String toString() { return name + "" + "(" + key + "): " + grade; } } 3) How to make it easy to have access to the database? Use a DAO * This is a design pattern that decouples the interface that access the data from the implementation necessary to establish a connection to the data. public interface DAO { V get(K key); void add(K key, V value); void delete(K key); void update(K key, V value); } 4.1) What is a typical use case in this application? - User want to check student grade - User tells Dao the student ID - Dao opens connection with Database - Dao sends ID to Database - If Database contains ID, then Database sends Student back to Dao - Dao reports the student back to the user. 4.2) Who are the actors? User, Dao, Database 4.3) Can you reproduce this behavior as a UML sequence diagram? User Dao Socket ServerSocket StudentHandlerThread |--get(long)-->| | | | | |---open-->| | | | | |----connect--->| | | |---send-->| | | | | |------send---->| | | | | |<------read-------| | | | |<--send(Student)--| | |---read-->| | | | | |------read---->| | | |--close-->| | | | | |-----close---->| | 5) What is a socket? * A socket is one endpoint of a two-way communication link between two programs running on the network. A socket is bound to a port number so that the TCP layer can identify the application that data is destined to be sent. 5.1) How to identify a socket? 6) We need something to listen for connections. How to implement it? 6.1) What are typical actions that must be taken when we receive a request? 6.2) Is it possible to handle multiple requests? public class GradeServer { public static void main(String[] args) { Map students = new HashMap(); loadDB(students); ServerSocket serverSocket = null; try { serverSocket = new ServerSocket(4444); while (true) { new StudentHandlerThread(serverSocket.accept(), students).start(); } serverSocket.close(); } catch (IOException e) { e.printStackTrace(); System.exit(-1); } } } 7) How the implementation of StudentHandlerThread would be like? public class StudentHandlerThread extends Thread { private Socket socket = null; Map students; public void addStudent(Student s) { students.put(s.key, s); } public StudentHandlerThread(Socket socket, Map students) { this.socket = socket; this.students = students; } public void run() { try { ObjectOutputStream out = new ObjectOutputStream(socket.getOutputStream()); ObjectInputStream in = new ObjectInputStream(socket.getInputStream()); Long key = (Long) in.readObject(); while (key.longValue() != 0L) { System.out.println("Received " + key); if (students.containsKey(key)) { out.writeObject(students.get(key)); } else { out.writeObject(new Student(-1, "", 0.0)); } key = (Long) in.readObject(); } out.close(); in.close(); socket.close(); } catch (Exception e) { e.printStackTrace(); } } } 8) How would an implementation be? public class Dao1 implements DAO { public Student get(Long key) { Student s = null; Socket socket; ObjectOutputStream out; ObjectInputStream in; try { socket = new Socket("localhost", 4444); out = new ObjectOutputStream(socket.getOutputStream()); in = new ObjectInputStream(socket.getInputStream()); out.writeObject(new Long(key)); s = (Student) in.readObject(); out.writeObject(new Long(0L)); socket.close(); out.close(); in.close(); } catch (Exception e) { e.printStackTrace(); } return s; } public void add(Long key, Student value) { } public void update(Long key, Student value) { } public void delete(Long key) { } public static void main(String args[]) { Dao1 d = new Dao1(); Student s = d.get(200934878L); System.out.println(s); } } 8.1) How to implement the other methods of the DAO interface? 8.2) How to distinguish one request from the other, in the server side? 8.3) What is the communication protocol in this case? DAO DataBase |-----GET:int---->| |----KEY:long---->| |<----Student-----| |-----EOF:int---->| 9) Take a look into dao.ex2, in ClassNotes25.txt. What are the problems with this implementation? - A lot of code replication. - The code of each method is clogged with socket connection details. 10) How to improve this implementation? - In principle we could improve it by factoring out the code that is in charge of creating connections. - Take a look into dao.ex3: public class Dao3 implements DAO { private Socket socket; private ObjectOutputStream out; private ObjectInputStream in; private static final int EOF = -1; private void openConnection(String host, int port) { try { socket = new Socket(host, port); out = new ObjectOutputStream(socket.getOutputStream()); in = new ObjectInputStream(socket.getInputStream()); } catch (Exception e) { e.printStackTrace(); } } private void closeConnection() { try { socket.close(); out.close(); in.close(); } catch (Exception e) { e.printStackTrace(); } } public Student get(Long key) { Student s = null; try { this.openConnection("localhost", 4444); out.writeObject(new Integer(DAO.GET)); out.writeObject(new Long(key)); s = (Student) in.readObject(); out.writeObject(new Integer(EOF)); this.closeConnection(); } catch (Exception e) { e.printStackTrace(); } return s; } ... } 10.1) What are the problems with Dao3? - Session per operation. 10.2) Is it possible to share the same socket between two different DAO's? 10.3) Is it possible to recycle connections among different DAO methods? 11) How can we solve these problems? - Create a method to open the connection, and another to close it. In this case, see dao.ex4. public static void main(String args[]) { System.out.println("Running the client 4"); Dao4 d = new Dao4(); d.openConnection("localhost", 4444); Student s = d.get(200934878L); System.out.println(s); d.add(160355385L, new Student(160355385L, "Caravaggio", 98.0)); s = d.get(160355385L); System.out.println(s); d.update(160355385L, new Student(160355385L, "Michelangelo Merisi", 99.5)); s = d.get(160355385L); System.out.println(s); d.closeConnection(); } 11.2) What is the problem with this last approach? 11.3) Where should the openConnection method be inserted? 11.4) What about closing the connection? How to enforce that the connection will be eventually closed? 11.5) What are the problems with the close() method? - reveals implementation. - moves responsibility to user of DAO - gives margin to errors. - AND, there is no way to share connections among DAO's... 12) How to solve all these problems? 13) Has anyone heard of the single responsibility principle? * The single responsibility principle - There should never be more than one reason for a class to change. 13.1) What is a responsility? public class Dao5 implements DAO { private ObjectOutputStream out; private ObjectInputStream in; private static final int EOF = -1; public Dao5(ObjectOutputStream out, ObjectInputStream in) { this.out = out; this.in = in; } ... } 14) Ok, the program in dao.ex5 solves the problem, but not quite. Which new problems have we added? - The code is hard to reuse. 15) Has anyone read about the command design pattern? - We have used a version of command in our implementatio of cloud computing. client------------------------->Invoker | | | ConcreteCommand | \ +execute() _____ | \ \ | \ V V \ Command ---------------------> +execute() 16) What would be the interface of a command? public interface Command { void execute(Dao6 d); } 17) What about the implementation of a typical command? public class CommandImpl implements Command { public void execute(Dao6 d) { // Execute the DAO operations Student s = d.get(200934878L); System.out.println(s); d.add(160355385L, new Student(160355385L, "Caravaggio", 98.0)); s = d.get(160355385L); System.out.println(s); d.update(160355385L, new Student(160355385L, "Michelangelo Merisi", 99.5)); s = d.get(160355385L); System.out.println(s); } } 16) What are the responsibilities of the invoker? 16.1) What are the parameters of the invoke method? public class Invoker { private static final int EOF = -1; private String host; private int port; private Socket socket = null; private ObjectOutputStream out = null; private ObjectInputStream in = null; public Invoker(String host, int port) { this.host = host; this.port = port; } private void openConnection() { try { socket = new Socket(host, port); out = new ObjectOutputStream(socket.getOutputStream()); in = new ObjectInputStream(socket.getInputStream()); } catch (Exception e) { e.printStackTrace(); } } private void closeConnection() { try { out.writeObject(new Integer(EOF)); out.flush(); socket.close(); out.close(); in.close(); } catch (Exception e) { e.printStackTrace(); } } public void invoke(List daos, Command c) { openConnection(); for (Dao6 d : daos) { d.setChannels(out, in); c.execute(d); } closeConnection(); } } 17) How to compile and run the whole thing? javac dao/*.java javac dao/ex6/*.java // On the server side: java dao.ex6.GradeServer // On the client side (same machine) java dao.ex6.Driver