package com.coder.client; import java.io.IOException; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.logging.Level; import java.util.logging.Logger; import com.coder.client.gui.GuiWindow; import com.coder.client.gui.editor.EditorWindow; import com.coder.client.gui.editor.EditorWindowListener; import com.coder.client.gui.login.LoginDialog; import com.coder.client.gui.login.LoginDialogListener; import com.coder.client.gui.newProject.NewProjectDialog; import com.coder.client.gui.newProject.NewProjectDialogListener; import com.coder.client.gui.selectProject.SelectProjectDialog; import com.coder.client.gui.selectProject.SelectProjectDialogListener; import com.coder.client.gui.selectServer.SelectServerDialog; import com.coder.client.gui.selectServer.SelectServerDialogListener; import com.coder.client.session.ProjectListRspMsgListener; import com.coder.client.session.ProjectRspMsgListener; import com.coder.client.session.ProjectTypeRspMsgListener; import com.coder.client.session.Session; import com.coder.client.session.SessionListener; import com.coder.server.message.*; import zutil.log.CompactLogFormatter; import zutil.log.LogUtil; import zutil.net.ssdp.SSDPClient; import zutil.net.ssdp.SSDPClient.SSDPServiceListener; import zutil.net.ssdp.StandardSSDPInfo; import javafx.application.Application; import javafx.application.Platform; import javafx.stage.Stage; public class CoderClient extends Application{ public static final Logger logger = LogUtil.getLogger(); private HashSet sessionListeners = new HashSet(); private Session session; private Stage mainStage; //GUI elements private EditorWindow editorWindow; private LoginDialog loginDialog; private SelectProjectDialog selectProjectDialog; private SelectServerDialog selectServerDialog; private NewProjectDialog newProjectDialog; //state variables private GuiWindow projectSelectionWindow = null; //points to the GUI that selected a project for the editor. If any error occurs while creating/loading the project, this is the window we will go back to. //services SSDPClient ssdpClient; public static void main(String[] args) { Application.launch(args); } @Override public void start(Stage mainStage) throws Exception { //setup logging CompactLogFormatter formatter = new CompactLogFormatter(); LogUtil.setLevel("com.coder", Level.FINEST); LogUtil.setFormatter("com.coder", formatter); LogUtil.setLevel("zutil", Level.FINEST); LogUtil.setFormatter("zutil", formatter); LogUtil.setGlobalFormatter(formatter); //setup GUI elements this.mainStage = mainStage; mainStage.setTitle("CoderClient"); try{ setupSelectServerDialog(); setupLoginDialog(); setupSelectProjectDialog(); setupEditWindow(); setupNewProjectDialog(); }catch(IOException e){ logger.log(Level.SEVERE, "could not load all GUI elements", e); System.exit(1); } //parse program arguments Map params = this.getParameters().getNamed(); for(String key : params.keySet()){ String value = params.get(key); if(key.equals("username")){ loginDialog.setUsername(value); }else if(key.equals("password")){ loginDialog.setPassword(value.toCharArray()); }else if(key.equals("url")){ selectServerDialog.setServerAddress(value); }else if(key.equals("port")){ try{ selectServerDialog.setServerPort(Integer.parseInt(value)); }catch(NumberFormatException e){ logger.warning("port argument to program is not of a integer type"); selectServerDialog.setServerPort(-1); } }else if(key.equals("project")){ selectProjectDialog.setProject(value); } } //setup SSDP client try{ setupSSDPClient(); }catch(IOException e){ logger.log(Level.SEVERE, "could not setup SSDP client", e); System.exit(1); } //start program logic selectServerDialog.setServerPort(-1); selectServerDialog.showOnStage(mainStage); } private void setupSelectServerDialog() throws IOException{ this.selectServerDialog = new SelectServerDialog(); this.selectServerDialog.addSelectProjectDialogListener(new SelectServerDialogListener() { @Override public void willShow() { logger.fine("about to show select server dialog on main stage"); closeCurrentSession(); selectServerDialog.clearServerList(); if(ssdpClient != null){ ssdpClient.requestService("coder:discover"); for(StandardSSDPInfo server : ssdpClient.getServices("coder:discover")) selectServerDialog.addServerToList(server.getInetAddress().getHostAddress(), 1337); }else{ logger.severe("could not send a SSDP request since the client is not setup"); } } @Override public void exit() { logger.info("terminating"); Platform.exit(); } @Override public void connect(String address, int port) { //connect session session = Session.setupConnection(address, port); if(session == null){ logger.warning("Could not setup a connection to " + address + ":" + port); selectServerDialog.setServerAddress(null); selectServerDialog.setErrorMessage("ERROR: Unable to connect to remote host"); loginDialog.setErrorMessage(""); selectServerDialog.showOnStage(mainStage); }else{ selectServerDialog.setErrorMessage(""); loginDialog.showOnStage(mainStage); } } }); } private void setupLoginDialog() throws IOException { this.loginDialog = new LoginDialog(); this.loginDialog.addLoginDialogListener(new LoginDialogListener(){ @Override public void willShow() { logger.fine("about to show login dialog on main stage"); } @Override public void cancel() { loginDialog.setErrorMessage(""); selectServerDialog.setServerAddress(null); selectServerDialog.showOnStage(mainStage); } @Override public void login(String username, char[] password) { //authenticate session boolean authenticated = session.authenticate(username, password); if(!authenticated){ logger.severe("Authentication failed: wrong username or password"); loginDialog.setPassword(null); loginDialog.setErrorMessage("Wrong username or password"); selectServerDialog.showOnStage(mainStage); return; }else{ loginDialog.setErrorMessage(""); setupSessionListener(); //resister a message listener to the session session.start(); //start receiving traffic from the server selectProjectDialog.showOnStage(mainStage); } } }); } /** * To be called after a session has been connected and authenticated */ private void setupSessionListener(){ if(session == null){ logger.warning("Cannot setup session listeners for null instance. ignoring call."); return; } if(!session.isAuthenticated()){ logger.warning("Cannot setup session listeners for a non-authenticated session. ignoring call."); return; } //indicate for listeners that a new session is up for(SessionListener listener : sessionListeners){ listener.sessionUp(session); } // create a guard for when the session is closed new Thread(new Runnable(){ @Override public void run() { logger.fine("starting a session guard"); while(true){ if(session == null || !session.isConnected()){ logger.fine("session guard: no connection"); Platform.runLater(new Runnable() { @Override public void run() { selectServerDialog.setErrorMessage("The current session was disconnected"); closeCurrentSession(); selectServerDialog.showOnStage(mainStage); } }); break; } Thread.yield(); } logger.fine("terminating session guard"); } }).start(); } private void setupSelectProjectDialog() throws IOException { this.selectProjectDialog = new SelectProjectDialog(); this.selectProjectDialog.addSelectProjectDialogListener(new SelectProjectDialogListener() { @Override public void willShow() { logger.fine("about to show select project dialog on main stage"); selectProjectDialog.clearProjectList(); if(!selectProjectDialog.isProjectSelected()){ sendProjectListReq(); } } @Override public void open(String selectedProjectName) { projectSelectionWindow = selectProjectDialog; editorWindow.showOnStage(mainStage); } @Override public void newProject() { selectProjectDialog.setProject(null); newProjectDialog.showOnStage(mainStage); } @Override public void cancel() { loginDialog.setPassword(null); selectServerDialog.setServerAddress(null); selectServerDialog.showOnStage(mainStage); } @Override public void refresh() { selectProjectDialog.clearProjectList(); sendProjectListReq(); } private void sendProjectListReq(){ //send a request for a new list of projects CoderMessage msg = new CoderMessage(); msg.ProjectListReq = new ProjectListReqMsg(); try { session.send(msg); } catch (IOException e) { logger.log(Level.SEVERE, "Unable to send ProjectListReqMsg", e); closeCurrentSession(); } } }); this.sessionListeners.add(new SessionListener() { @Override public void sessionUp(Session session) { session.addProjectListRspMsgListener(new ProjectListRspMsgListener() { @Override public void messageReceived(final ProjectListRspMsg msg) { logger.fine("a ProjectListRsp received"); Platform.runLater(new Runnable() { @Override public void run() { for(String projectName : msg.keySet()){ ProjectListData projectData = msg.get(projectName); selectProjectDialog.addProjectToList(projectName, projectData); } } }); } }); } }); } private void setupNewProjectDialog() throws IOException { this.newProjectDialog = new NewProjectDialog(); this.newProjectDialog.addNewProjectDialogListener(new NewProjectDialogListener(){ @Override public void willShow() { logger.fine("about to show new project dialog on main stage"); newProjectDialog.clearProjectTypeList(); sendProjectTypeReqMsg(); } @Override public void create(String newProjectName, String projectType, String projectDescription) { projectSelectionWindow = newProjectDialog; sendProjectCreateReqMsg(newProjectName, projectType, projectDescription); selectProjectDialog.setProject(newProjectName); editorWindow.showOnStage(mainStage); } private void sendProjectTypeReqMsg(){ CoderMessage msg = new CoderMessage(); msg.ProjectTypeReq = new ProjectTypeReqMsg(); try { session.send(msg); } catch (IOException e) { logger.log(Level.SEVERE, "Unable to send ProjectTypeReq", e); closeCurrentSession(); } } private void sendProjectCreateReqMsg(String projectName, String projectType, String projectDescription){ CoderMessage msg = new CoderMessage(); msg.ProjectCreateReq = new ProjectCreateReqMsg(); msg.ProjectCreateReq.name = projectName; msg.ProjectCreateReq.type = projectType; msg.ProjectCreateReq.description = projectDescription; try { session.send(msg); } catch (IOException e) { logger.log(Level.SEVERE, "Unable to send ProjectCreateReq", e); closeCurrentSession(); } } @Override public void cancel() { selectProjectDialog.setProject(null); selectProjectDialog.showOnStage(mainStage); } }); this.sessionListeners.add(new SessionListener() { @Override public void sessionUp(Session session) { session.addProjectTypeRspMsgListener(new ProjectTypeRspMsgListener() { @Override public void messageReceived(final ProjectTypeRspMsg msg) { logger.fine("a ProjectTypeRspMsg received"); Platform.runLater(new Runnable() { @Override public void run() { for(String typeName : msg.keySet()){ SupportedProperties typeData = msg.get(typeName); newProjectDialog.addProjectTypeToList(typeName, typeData); } } }); } }); } }); } private void setupEditWindow() throws IOException { this.editorWindow = new EditorWindow(); this.editorWindow.addEditorWindowListener(new EditorWindowListener() { @Override public void willShow() { logger.fine("about to show edit window on main stage"); sendProjectReqMsg(); } @Override public void compile() { //TODO } @Override public void run() { //TODO } private void sendProjectReqMsg(){ if(!selectProjectDialog.isProjectSelected()){ logger.severe("Cannot send a project request when no project is selected"); return; } CoderMessage msg = new CoderMessage(); msg.ProjectReq = new ProjectReqMsg(); msg.ProjectReq.name = selectProjectDialog.getSelectedProject(); try { session.send(msg); } catch (IOException e) { logger.log(Level.SEVERE, "Unable to send ProjectReqMsg", e); closeCurrentSession(); } } @Override public void changeProject() { selectProjectDialog.setProject(null); selectProjectDialog.showOnStage(mainStage); } @Override public void exit() { closeCurrentSession(); Platform.exit(); } }); sessionListeners.add(new SessionListener() { @Override public void sessionUp(Session session) { session.addProjectRspMsgListener(new ProjectRspMsgListener() { @Override public void messageReceived(final ProjectRspMsg msg) { logger.fine("a ProjectRspMsg received"); Platform.runLater(new Runnable() { @Override public void run() { if(msg.error != null){ logger.severe("Server responded on the project request with the following error message: " + msg.error); selectProjectDialog.setProject(null); if(projectSelectionWindow != null){ projectSelectionWindow.setErrorMessage("ERROR: " + msg.error); projectSelectionWindow.showOnStage(mainStage); return; }else{ selectProjectDialog.setErrorMessage("ERROR: " + msg.error); selectProjectDialog.showOnStage(mainStage); return; } }else{ if(projectSelectionWindow != null){ projectSelectionWindow.setErrorMessage(""); } editorWindow.setProjectName(msg.name); Properties projectConfig = msg.config; String projectDescription = msg.description; List fileList = msg.fileList; String projectType = msg.type; //TODO: handle msg } } }); } }); session.addProjectTypeRspMsgListener(new ProjectTypeRspMsgListener() { @Override public void messageReceived(ProjectTypeRspMsg msg) { //TODO: update the project properties } }); } }); } private void setupSSDPClient() throws IOException{ this.ssdpClient = new SSDPClient(); ssdpClient.setListener(new SSDPServiceListener() { @Override public void newService(final StandardSSDPInfo service) { if(selectServerDialog != null){ Platform.runLater(new Runnable() { @Override public void run() { String ip = service.getInetAddress().getHostAddress(); selectServerDialog.addServerToList(ip, 1337); } }); } else { logger.warning("New Service found ("+service.getLocation()+") when stage closed."); } } }); ssdpClient.start(); } private void closeCurrentSession(){ if(this.session != null){ logger.info("disconnecting from server"); session.close(); session = null; } } }