package no.ntnu.fp.net.network.server; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.io.ObjectOutputStream; import java.net.Socket; import java.sql.SQLException; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Queue; import java.util.Set; import org.jdom.adapters.XML4JDOMAdapter; import no.ntnu.fp.storage.db.DatabaseController; import no.ntnu.fp.util.Log; import no.ntnu.fp.model.Appointment; import no.ntnu.fp.model.Authenticate; import no.ntnu.fp.model.Meeting; import no.ntnu.fp.model.Notification; import no.ntnu.fp.model.Place; import no.ntnu.fp.model.Room; import no.ntnu.fp.model.User; import no.ntnu.fp.model.XmlHandler; import no.ntnu.fp.model.Meeting.State; import no.ntnu.fp.net.network.Request; import no.ntnu.fp.net.network.Request.Method; import no.ntnu.fp.net.network.Tuple; import nu.xom.ParsingException; import nu.xom.ValidityException; //TODO: Communicate with the db public class ServerController { // fields private Map<String, Socket> connectedClients; private ArrayList<String> participants; private DatabaseController databaseController; private XmlHandler xmlHandler; private Queue<Tuple<Socket, Object>> inQueue; private Map<String, ArrayList<String>> views; // Constructor public ServerController(Map<String, Socket> clients, Queue<Tuple<Socket, Object>> inQueue) { databaseController = new DatabaseController(); connectedClients = clients; this.inQueue = inQueue; views = new HashMap<String, ArrayList<String>>(); System.out.println("GetSubscribers"); getSubscribers(); } /** * Helper method for sending over objects over TCP * **/ private void send(Socket socket, Object data) { try { DataOutputStream os = new DataOutputStream(socket.getOutputStream()); ObjectOutputStream oos = new ObjectOutputStream(os); oos.writeObject(data); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } // TODO: Call this method when the server starts private void getSubscribers() { try { List<Tuple<String, String>> list = databaseController.getSubscribers(); for (Tuple<String, String> t : list) { if (views.containsKey(t.y)) { views.get(t.x).add(t.x); } else { ArrayList<String> l = new ArrayList<String>(); l.add(t.x); views.put(t.y, l); } } } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } /** * Authenticate the user by checking the database. Sends a * login succeded if the login credentials are correct, or login * failed otherwise. * **/ public void authenticate(Tuple<Socket, Object> data) { try { Request request = (Request) data.y; Authenticate auth = request.getAuth(); String username = auth.getUsername(); String password = auth.getPassword(); System.out.println("Username: " + username); System.out.println("Password: " + password); // Check username and password againts the database if (databaseController.authenticate(username, password)) { connectedClients.put(username, data.x); Request response = new Request(null, null); response.setMethod(Request.Method.LOGIN_SUCCEDED); send(data.x, response); System.out.println("Login completed"); } else { System.out.println("Login failed"); Request response = new Request(null, null); response.setMethod(Request.Method.LOGIN_FAILED); send(data.x, response); } } catch (SQLException sq) { sq.printStackTrace(); } } /** * Get all user from the database. If the user who requested * the users is authenticated, send the users and a * GET_USERS_RESPONSE Flag, otherwise send LOGIN_FAILED Flag * * **/ public void getUsers(Tuple<Socket, Object> data) { try { Request request = (Request) data.y; Authenticate auth = request.getAuth(); System.out.println("GetUser user: " + auth.getUsername()); if (connectedClients.containsKey(auth.getUsername())) { List<User> users = databaseController.getListOfUsers(); Request response = new Request(null, (Object) users); response.setMethod(Request.Method.GET_USERS_RESPONSE); // send the data to the client send(data.x, response); } else { Request response = new Request(null, null); response.setMethod(Request.Method.LOGIN_FAILED); send(data.x, response); } } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } /**Whenever a user requests a calendar the owner of the calendar * is mapped to the user who requested the calendar. * **/ private void markViewed(String requestFrom, String viewUser) throws SQLException { // a user can view more than on calendar if (!requestFrom.equals(viewUser)) { if (views.containsKey(viewUser)) { ArrayList<String> list = views.get(viewUser); list.add(requestFrom); } else { ArrayList<String> list = new ArrayList<String>(); list.add(requestFrom); views.put(viewUser, list); System.out.println("request From (Value): " + requestFrom); System.out.println("view user (key)" + viewUser); } System.out.println("Apply changes"); databaseController.subscribeToCalendar(requestFrom, viewUser); } } /** * Sends notfications to the viewers of a calendar whenever * the owner changes the calendar * **/ private void sendChangesToViewers(String username, Object data, Request.Method method) { if (views.containsKey(username)) { ArrayList<String> list = views.get(username); for (String s : list) { if (connectedClients.containsKey(s)) { System.out.println("Send changes to: "+s); Socket sockfd = connectedClients.get(s); Request response = new Request(null, data); response.setMethod(method); send(sockfd, response); } } } } /** * Get full user from the database If the user, who requested * the message is authenticated, send the User and a * GET_FULL_USERS_RESPONSE message, send a LOGIN_FAILED message * otherwise * * **/ // TODO: Implements public void getFullUser(Tuple<Socket, Object> data) { try { Request request = (Request) data.y; Authenticate auth = request.getAuth(); String username = (String) request.getObject(); if (connectedClients.containsKey(auth.getUsername())) { // TODO:test markViewed(auth.getUsername(), username); User user = databaseController.getFullUser(username); Request response = new Request(null, user); System.out.println("User " + user.getName()); response.setMethod(Request.Method.GET_FULL_USER_RESPONSE); send(data.x, response); } else { Request response = new Request(null, null); response.setMethod(Request.Method.LOGIN_FAILED); send(data.x, XmlHandler.loginUnsucessful()); } } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } /** * @author bj0rn Save meeting Save received meeting object, if the user whom * sent the object is authenticated. * * * **/ public void saveMeeting(Tuple<Socket, Object> data) { try { Request request = (Request) data.y; Meeting meeting = (Meeting) request.getObject(); String username = meeting.getOwner().getUsername(); System.out.println("Owner: " + username); if (connectedClients.containsKey(username)) { Integer id = databaseController.saveMeeting(meeting); meeting.setID(id); // send key to owner Request response = new Request(null, id); response.setMethod(Request.Method.SAVE_MEETING_RESPONSE); send(data.x, response); System.out.println("Send data to connected clients"); // TODO: Does this work sendChangesToViewers(username, meeting, Method.CHANGE_MEETING_NOTFICATION); // also send message to available clients System.out.println("Send data to connected clients"); Set<User> participants = meeting.getParticipants(); for (User u : participants) { String user = u.getUsername(); System.out.println("Participant: " + user); if (connectedClients.containsKey(user)) { System.out.println("Works"); Socket sockfd = connectedClients.get(user); Request r = new Request(null, meeting); r.setMethod(Request.Method.MEETING_NOTIFICATION); send(sockfd, r); System.out.println("Data is sent"); } else { // do some stuff in the db ? System.out.println("Sorry the client is not connected"); } } } } catch (SQLException sq) { sq.printStackTrace(); } } /** * @author bj0rn * * * **/ public void saveAppointment(Tuple<Socket, Object> data) { // Got an appointment object try { Request request = (Request) data.y; Authenticate auth = request.getAuth(); Appointment a = (Appointment) request.getObject(); System.out.println("user: " + auth.getUsername()); if (connectedClients.containsKey(auth.getUsername())) { Integer id = databaseController.saveAppointment(a); a.setID(id); Request response = new Request(null, id); response.setMethod(Request.Method.SAVE_APPOINTMENT_RESPONSE); send(data.x, response); // TODO: does this work sendChangesToViewers(auth.getUsername(), a, Method.CHANGE_APPOINTMENT_NOTIFICATION); } else { // Not authenticated Request response = new Request(null, null); response.setMethod(Request.Method.LOGIN_FAILED); send(data.x, response); } } catch (SQLException sq) { sq.printStackTrace(); } } /** * Helper method for sending meeting reply�s * Used in dispatchMeetingReply() * **/ private void sendMeeting(String user, Meeting meeting){ if(connectedClients.containsKey(user)){ Request response = new Request(null, meeting); response.setMethod(Method.MEETING_REPLY); send(connectedClients.get(user), response); } } /** * * * **/ public void dispatchMeetingReply(Tuple<Socket, Object> data) { try { Request request = (Request) data.y; Authenticate auth = request.getAuth(); if (connectedClients.containsKey(auth.getUsername())) { String xml = (String) request.getObject(); ArrayList<String> dataValues = (ArrayList<String>) XmlHandler .dispatchMeetingReplyFromXml(xml); String username = dataValues.get(0); System.out.println("Userid " + username); String meetingId = dataValues.get(1); System.out.println("MeetingId " + meetingId); String state = dataValues.get(2); System.out.println("State " + state); // Check with db if (databaseController.updateMeetingState(username, meetingId, State.getState(state))) { // send success Request response = new Request(null, null); response.setMethod(Method.SAVE_APPOINTMENT_RESPONSE); send(data.x, response); Meeting meeting = databaseController.getMeeting(Integer.parseInt(meetingId)); String owner = meeting.getOwner().getUsername(); sendMeeting(owner, meeting); Set <User> participants = meeting.getParticipants(); for(User p: participants){ sendMeeting(p.getUsername(), meeting); } } else { Request response = new Request(null, null); response.setMethod(Method.LOGIN_FAILED); send(data.x, response); } } } catch (SQLException sq) { sq.printStackTrace(); } } /** * Sends a list of rooms to the client who initially made * the request * * **/ public void getListOfRooms(Tuple<Socket, Object> data) { try { Request request = (Request) data.y; Authenticate auth = request.getAuth(); if (connectedClients.containsKey(auth.getUsername())) { List<Room> listRooms = databaseController.getListOfRooms(); if(listRooms == null){ System.out.println("Smack! You got served"); } Request response = new Request(null, listRooms); response.setMethod(Method.GET_LIST_OF_ROOMS_RESPONSE); System.out.println("Boom shakalakka"); send(data.x, response); } else { Request response = new Request(null, null); response.setMethod(Method.LOGIN_FAILED); send(data.x, response); } } catch (SQLException sq) { sq.printStackTrace(); } } /** * Cancels the view of a calendar specified by the client who * initially made the request. * **/ public void cancelView(Tuple<Socket, Object> data) { try { Request request = (Request) data.y; String username = request.getAuth().getUsername(); String cancelViewOfUser = (String) request.getObject(); if (connectedClients.containsKey(username)) { System.out.println("Remove from cache"); views.remove(cancelViewOfUser); System.out.println("Remove from db"); databaseController.unsubscribeToCalendar(username, cancelViewOfUser); Request response = new Request(null, null); response.setMethod(Method.CANCEL_VIEW_SUCCEDED); send(data.x, response); } else { Request response = new Request(null, null); response.setMethod(Method.LOGIN_FAILED); send(data.x, response); } } catch (SQLException sq) { sq.printStackTrace(); } } /** * Delete a meeting from the database. The delete request is * sent from a client * * */ public void deleteMeeting(Tuple<Socket, Object> data) { try { Request request = (Request) data.y; String username = request.getAuth().getUsername(); if (connectedClients.containsKey(username)) { Integer id = (Integer) request.getObject(); //SO SUE MEE!!!! Meeting meeting = databaseController.getMeeting(id); Log.out(id); databaseController.deleteMeeting(id); // send response ? Request response = new Request(null, id); response.setMethod(Method.DELETE_MEETING_RESPONSE); send(data.x, response); Set <User> participants = meeting.getParticipants(); sendChangesToViewers(username, id, Method.DELETE_MEETING_RESPONSE); for(User u : participants){ if(connectedClients.containsKey(u.getUsername())){ send(connectedClients.get(u.getUsername()), response); } } } } catch (SQLException sq) { sq.printStackTrace(); } } public void deleteAppointment(Tuple<Socket, Object> data) { try { Request request = (Request) data.y; String username = request.getAuth().getUsername(); if (connectedClients.containsKey(username)) { Integer id = (Integer) request.getObject(); databaseController.deleteAppointment(id); Request response = new Request(null, id); send(data.x, response); sendChangesToViewers(username, id, Method.DELETE_MEETING_RESPONSE); } else { // Hmm ? } } catch (SQLException sq) { sq.printStackTrace(); } } public void updateSelectedUsers(Tuple <Socket, Object> data){ try { Request request = (Request)data.y; String username = request.getAuth().getUsername(); if(connectedClients.containsKey(username)){ ArrayList<Tuple <String, String>> list = (ArrayList<Tuple<String, String>>) databaseController.getSubscribers(); List<User> res = new ArrayList<User>(); for(Tuple <String, String> t : list){ String user = t.x; String views = t.y; if(user.equals(username)){ System.out.println("User: "+user); System.out.println("Views: "+views); res.add(new User(views)); } } Request response = new Request(null, res); response.setMethod(Method.GET_SUBSCRIBERS_RESPONSE); send(data.x, response); }else { Request response = new Request(null, null); response.setMethod(Method.LOGIN_FAILED); send(data.x, response); } }catch (SQLException sq){ sq.printStackTrace(); } } public void inspectRequest(Tuple<Socket, Object> data) { Class<? extends Object> clazz = data.y.getClass(); String objectName = clazz.getSimpleName(); System.out.println("ObjectName: " + objectName); // This should be a request Request request = (Request) data.y; Request.Method requestType = request.getMethod(); if (requestType == Request.Method.AUTHENTICATE) { System.out.println("Ready for magic"); authenticate(data); } else if (requestType == Request.Method.GET_USERS) { System.out.println("Ready for magic getUsers"); getUsers(data); } else if (requestType == Request.Method.GET_FULL_USER) { System.out.println("readu for magic getFullUser"); getFullUser(data); } else if (requestType == Request.Method.SAVE_MEETING) { System.out.println("Ready for magic saveMeeting"); saveMeeting(data); } else if (requestType == Request.Method.SAVE_APPOINTMENT) { System.out.println("Ready for magic; save appointment"); saveAppointment(data); } else if (requestType == Method.DISPATCH_MEETING_REPLY) { System.out.println("Ready for magic; DispatchMeetingReply"); dispatchMeetingReply(data); }else if(requestType == Method.GET_LIST_OF_ROOMS){ System.out.println("Ready for magic; getListOfRooms"); getListOfRooms(data); }else if(requestType == Method.CANCEL_VIEW){ System.out.println("Ready for magic; cancelView"); System.out.println("Not tested yet"); cancelView(data); }else if(requestType == Method.DELETE_MEETING){ System.out.println("Enter delete meeting"); deleteMeeting(data); }else if(requestType == Method.DELETE_APPOINTMENT){ System.out.println("Enter delete appointment"); deleteAppointment(data); }else if(requestType == Method.GET_SUBSCRIBERS){ System.out.println("Get subscribers "); updateSelectedUsers(data); } } }