Mercurial > hg > accesspd
view java/src/uk/ac/qmul/eecs/ccmi/network/ClientConnectionManager.java @ 1:e3935c01cde2 tip
moved license of PdPersistenceManager to the beginning of the file
author | Fiore Martin <f.martin@qmul.ac.uk> |
---|---|
date | Tue, 08 Jul 2014 19:52:03 +0100 |
parents | 78b7fc5391a2 |
children |
line wrap: on
line source
/* CCmI Editor - A Collaborative Cross-Modal Diagram Editing Tool Copyright (C) 2011 Queen Mary University of London (http://ccmi.eecs.qmul.ac.uk/) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ package uk.ac.qmul.eecs.ccmi.network; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.io.IOException; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.SocketChannel; import java.text.MessageFormat; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.Map; import java.util.ResourceBundle; import java.util.Set; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.LinkedBlockingQueue; import javax.swing.SwingUtilities; import uk.ac.qmul.eecs.ccmi.diagrammodel.DiagramElement; import uk.ac.qmul.eecs.ccmi.diagrammodel.DiagramTreeNode; import uk.ac.qmul.eecs.ccmi.gui.Diagram; import uk.ac.qmul.eecs.ccmi.gui.DiagramEventSource; import uk.ac.qmul.eecs.ccmi.gui.DiagramPanel; import uk.ac.qmul.eecs.ccmi.gui.Edge; import uk.ac.qmul.eecs.ccmi.gui.EditorTabbedPane; import uk.ac.qmul.eecs.ccmi.gui.Finder; import uk.ac.qmul.eecs.ccmi.gui.Node; import uk.ac.qmul.eecs.ccmi.gui.SpeechOptionPane; import uk.ac.qmul.eecs.ccmi.gui.awareness.DisplayFilter; import uk.ac.qmul.eecs.ccmi.speech.Narrator; import uk.ac.qmul.eecs.ccmi.speech.NarratorFactory; import uk.ac.qmul.eecs.ccmi.utils.InteractionLog; /** * This is the class that manages the connection with the server. When a diagram is shared * this class becomes responsible for actually operating the model (trough the EVT tough, by calling SwingUtilities.invokeLater ). * If the operation is issued by the local user, than it performs the local action with local data only after * being acknowledged by the server, else it creates the data on demand. For example upon an insert * issued by the server, the element is created from scratch, according to the message of the server. * */ public class ClientConnectionManager extends NetworkThread { public ClientConnectionManager(EditorTabbedPane tabbedPane) throws IOException{ super("Network Client Thread"); channels = new HashMap<SocketChannel, Diagram>(); requests = new ConcurrentLinkedQueue<Request>(); answers = new LinkedBlockingQueue<Answer>(); pendingCommands = new LinkedList<SendCmdRequest>(); selector = Selector.open(); this.tabbedPane = tabbedPane; protocol = ProtocolFactory.newInstance(); mustSayGoodbye = false; waitingAnswer = false; } /** * The Event Dispatching Thread communicates with this thread through a concurrent queue. * This is the method to add requests to the queue. * @param r the request for this thread */ public void addRequest(Request r){ requests.add(r); if(r instanceof SendLockRequest){ SendLockRequest slr = (SendLockRequest) r; if(slr.lock.getName().toString().startsWith(LockMessage.GET_LOCK_PREFIX)) waitingAnswer = true; } selector.wakeup(); } public Answer getAnswer(){ try { Answer answer = answers.take(); waitingAnswer = false; return answer; } catch (InterruptedException e) { throw new RuntimeException(e);// must never happen } } @Override public void run(){ while(!mustSayGoodbye){ try { selector.select(); } catch (IOException e) { revertAllDiagrams(); } if(mustSayGoodbye) break; /* handle the requests for the remote server from the local users */ handleRequests(); for (Iterator<SelectionKey> itr = selector.selectedKeys().iterator(); itr.hasNext();){ SelectionKey key = itr.next(); itr.remove(); if(!key.isValid()) continue; if(key.isReadable()){ SocketChannel channel = (SocketChannel)key.channel(); Message msg = null; try { msg = protocol.receiveMessage(channel); } catch (IOException e) { revertDiagram(channel,false); /* signal the event dispatching thread, otherwise blocked */ try { /* RevertedDiagramAnswer is to prevent the Event dispatching Thread from blocking if the * * server goes down and the client is still waiting for an answer. If the thread * * was not waiting for an answers the RevertedDiagramAnswer will not be put in the queue */ if(waitingAnswer) answers.put(new RevertedDiagramAnswer()); } catch (InterruptedException ie) { throw new RuntimeException(ie); } continue; } //System.out.println("ClientConnaectionManager: read message " + msg.getName()); /* retrieve the diagram */ String diagramName = msg.getDiagram(); final Diagram diagram = channels.get(channel); node = null; edge = null; if(msg instanceof Command){ final Command cmd = (Command)msg; cmd.getSource().setDiagramName(diagram.getName()); /* log the command through the interaction log, if any */ Command.log(cmd, "remote command received"); switch(cmd.getName()){ case INSERT_NODE : double dx = (Double)cmd.getArgAt(1); double dy = (Double)cmd.getArgAt(2); node = Finder.findNode((String)cmd.getArgAt(0), diagram.getNodePrototypes()); node = (Node)node.clone(); /* Place the top left corner of the bounds at the origin. It might be different from * * the origin, as it depends on how the clonation is implemented internally. These calls * * to translate are not notified to any listener as the node is not n the model yet */ Rectangle2D bounds = node.getBounds(); node.translate(new Point2D.Double(),-bounds.getX(),-bounds.getY(), DiagramEventSource.NONE); /* perform the actual translation from the origin */ node.translate(new Point2D.Double(),dx,dy,DiagramEventSource.NONE); SwingUtilities.invokeLater(new CommandExecutor.Insert(diagram.getCollectionModel(), node, null, cmd.getSource())); break; case INSERT_EDGE : diagram.getCollectionModel().getMonitor().lock(); edge = Finder.findEdge((String)cmd.getArgAt(0), diagram.getEdgePrototypes()); edge = (Edge)edge.clone(); long[] nodesToConnect = new long[cmd.getArgNum()-1]; for(int i=0;i<nodesToConnect.length;i++) nodesToConnect[i] = (Long)cmd.getArgAt(i+1); SwingUtilities.invokeLater(new CommandExecutor.Insert(diagram.getCollectionModel(), edge, nodesToConnect, cmd.getSource())); diagram.getCollectionModel().getMonitor().unlock(); break; case REMOVE_NODE : diagram.getCollectionModel().getMonitor().lock(); node = Finder.findNode((Long)cmd.getArgAt(0), diagram.getCollectionModel().getNodes()); diagram.getCollectionModel().getMonitor().unlock(); SwingUtilities.invokeLater(new CommandExecutor.Remove(diagram.getCollectionModel(),node,cmd.getSource())); break; case REMOVE_EDGE : diagram.getCollectionModel().getMonitor().lock(); edge = Finder.findEdge((Long)cmd.getArgAt(0), diagram.getCollectionModel().getEdges()); diagram.getCollectionModel().getMonitor().unlock(); SwingUtilities.invokeLater(new CommandExecutor.Remove(diagram.getCollectionModel(),edge,cmd.getSource())); break; case SET_EDGE_NAME : diagram.getCollectionModel().getMonitor().lock(); edge = Finder.findEdge((Long)cmd.getArgAt(0), diagram.getCollectionModel().getEdges()); diagram.getCollectionModel().getMonitor().unlock(); SwingUtilities.invokeLater(new CommandExecutor.SetName(edge, (String)cmd.getArgAt(1),cmd.getSource())); break; case SET_NODE_NAME : diagram.getCollectionModel().getMonitor().lock(); node = Finder.findNode((Long)cmd.getArgAt(0), diagram.getCollectionModel().getNodes()); diagram.getCollectionModel().getMonitor().unlock(); SwingUtilities.invokeLater(new CommandExecutor.SetName(node, (String)cmd.getArgAt(1),cmd.getSource())); break; case SET_PROPERTY : diagram.getCollectionModel().getMonitor().lock(); node = Finder.findNode((Long)cmd.getArgAt(0),diagram.getCollectionModel().getNodes()); diagram.getCollectionModel().getMonitor().unlock(); SwingUtilities.invokeLater(new CommandExecutor.SetProperty( node, (String)cmd.getArgAt(1), (Integer)cmd.getArgAt(2), (String)cmd.getArgAt(3), cmd.getSource() )); break; case SET_PROPERTIES : diagram.getCollectionModel().getMonitor().lock(); node = Finder.findNode((Long)cmd.getArgAt(0),diagram.getCollectionModel().getNodes()); diagram.getCollectionModel().getMonitor().unlock(); SwingUtilities.invokeLater(new CommandExecutor.SetProperties( node, (String)cmd.getArgAt(1), cmd.getSource() )); break; case CLEAR_PROPERTIES : diagram.getCollectionModel().getMonitor().lock(); node = Finder.findNode((Long)cmd.getArgAt(0),diagram.getCollectionModel().getNodes()); diagram.getCollectionModel().getMonitor().unlock(); SwingUtilities.invokeLater(new CommandExecutor.ClearProperties(node,cmd.getSource())); break; case SET_NOTES : int[] path = new int[cmd.getArgNum()-1]; for(int i = 0; i< cmd.getArgNum()-1;i++){ path[i] = (Integer)cmd.getArgAt(i); } final String notes = (String)cmd.getArgAt(cmd.getArgNum()-1); diagram.getCollectionModel().getMonitor().lock(); treeNode = Finder.findTreeNode(path, (DiagramTreeNode)diagram.getTreeModel().getRoot()); diagram.getCollectionModel().getMonitor().unlock();; SwingUtilities.invokeLater(new CommandExecutor.SetNotes(diagram.getTreeModel(),treeNode, notes,cmd.getSource())); break; case ADD_PROPERTY : diagram.getCollectionModel().getMonitor().lock(); node = Finder.findNode((Long)cmd.getArgAt(0),diagram.getCollectionModel().getNodes()); diagram.getCollectionModel().getMonitor().unlock(); SwingUtilities.invokeLater(new CommandExecutor.AddProperty( node, (String)cmd.getArgAt(1), (String)cmd.getArgAt(2), cmd.getSource() )); break; case REMOVE_PROPERTY : diagram.getCollectionModel().getMonitor().lock(); node = Finder.findNode((Long)cmd.getArgAt(0),diagram.getCollectionModel().getNodes()); diagram.getCollectionModel().getMonitor().unlock(); SwingUtilities.invokeLater(new CommandExecutor.RemoveProperty( node, (String)cmd.getArgAt(1), (Integer)cmd.getArgAt(2), cmd.getSource())); break; case SET_MODIFIERS : diagram.getCollectionModel().getMonitor().lock(); node = Finder.findNode((Long)cmd.getArgAt(0),diagram.getCollectionModel().getNodes()); indexes = new HashSet<Integer>(cmd.getArgNum()-3); for(int i=3;i<cmd.getArgNum();i++){ indexes.add((Integer)cmd.getArgAt(i)); } diagram.getCollectionModel().getMonitor().unlock(); SwingUtilities.invokeLater(new CommandExecutor.SetModifiers( node, (String)cmd.getArgAt(1), (Integer)cmd.getArgAt(2), indexes, cmd.getSource())); break; case SET_ENDLABEL : diagram.getCollectionModel().getMonitor().lock(); edge = Finder.findEdge((Long)cmd.getArgAt(0),diagram.getCollectionModel().getEdges()); node = Finder.findNode((Long)cmd.getArgAt(1),diagram.getCollectionModel().getNodes()); diagram.getCollectionModel().getMonitor().unlock(); SwingUtilities.invokeLater(new CommandExecutor.SetEndLabel(edge, node,(String)cmd.getArgAt(2),cmd.getSource())); break; case SET_ENDDESCRIPTION : diagram.getCollectionModel().getMonitor().lock(); edge = Finder.findEdge((Long)cmd.getArgAt(0),diagram.getCollectionModel().getEdges()); node = Finder.findNode((Long)cmd.getArgAt(1),diagram.getCollectionModel().getNodes()); diagram.getCollectionModel().getMonitor().unlock(); SwingUtilities.invokeLater(new CommandExecutor.SetEndDescription( edge, node, (Integer)cmd.getArgAt(2), cmd.getSource() )); break; case TRANSLATE_NODE : diagram.getCollectionModel().getMonitor().lock(); node = Finder.findNode((Long)cmd.getArgAt(0),diagram.getCollectionModel().getNodes()); diagram.getCollectionModel().getMonitor().unlock(); SwingUtilities.invokeLater(new CommandExecutor.Translate( node, new Point2D.Double((Double)cmd.getArgAt(1),(Double)cmd.getArgAt(2)), (Double)cmd.getArgAt(3), (Double)cmd.getArgAt(4), cmd.getSource())); break; case TRANSLATE_EDGE : diagram.getCollectionModel().getMonitor().lock(); edge = Finder.findEdge((Long)cmd.getArgAt(0),diagram.getCollectionModel().getEdges()); diagram.getCollectionModel().getMonitor().unlock(); SwingUtilities.invokeLater(new CommandExecutor.Translate( edge, new Point2D.Double((Double)cmd.getArgAt(1),(Double)cmd.getArgAt(2)), (Double)cmd.getArgAt(3), (Double)cmd.getArgAt(4), cmd.getSource())); break; case BEND : diagram.getCollectionModel().getMonitor().lock(); edge = Finder.findEdge((Long)cmd.getArgAt(0),diagram.getCollectionModel().getEdges()); diagram.getCollectionModel().getMonitor().unlock(); Point2D bendStart = null; if(cmd.getArgNum() == 5){ bendStart = new Point2D.Double((Double)cmd.getArgAt(3),(Double)cmd.getArgAt(4)); } SwingUtilities.invokeLater(new CommandExecutor.Bend( edge, new Point2D.Double((Double)cmd.getArgAt(1),(Double)cmd.getArgAt(2)), bendStart, cmd.getSource())); break; case STOP_EDGE_MOVE : diagram.getCollectionModel().getMonitor().lock(); edge = Finder.findEdge((Long)cmd.getArgAt(0),diagram.getCollectionModel().getEdges()); diagram.getCollectionModel().getMonitor().unlock(); SwingUtilities.invokeLater(new CommandExecutor.StopMove(edge,cmd.getSource())); break; case STOP_NODE_MOVE : node = Finder.findNode((Long)cmd.getArgAt(0), diagram.getCollectionModel().getNodes()); SwingUtilities.invokeLater(new CommandExecutor.StopMove(node,cmd.getSource())); break; } }else if(msg instanceof Reply){ Reply reply = (Reply)msg; /* log the reply through the interaction logger, if any */ Reply.log(reply); sendCmdRequest = null; for(SendCmdRequest scr : pendingCommands){ if(scr.matches(channel, reply.getDiagram())){ sendCmdRequest = scr; break; } } assert(sendCmdRequest != null); pendingCommands.remove(sendCmdRequest); switch(reply.getName()){ case INSERT_NODE_R : case INSERT_EDGE_R : SwingUtilities.invokeLater(new CommandExecutor.Insert(diagram.getCollectionModel(), sendCmdRequest.element, null, reply.getSource())); break; case REMOVE_EDGE_R : case REMOVE_NODE_R : SwingUtilities.invokeLater(new CommandExecutor.Remove(diagram.getCollectionModel(), sendCmdRequest.element,reply.getSource())); break; case SET_EDGE_NAME_R : case SET_NODE_NAME_R : SwingUtilities.invokeLater(new CommandExecutor.SetName(sendCmdRequest.element, (String)sendCmdRequest.cmd.getArgAt(1),reply.getSource())); break; case SET_PROPERTY_R : SwingUtilities.invokeLater(new CommandExecutor.SetProperty( (Node)sendCmdRequest.element, (String)sendCmdRequest.cmd.getArgAt(1), (Integer)sendCmdRequest.cmd.getArgAt(2), (String)sendCmdRequest.cmd.getArgAt(3), reply.getSource() )); break; case SET_PROPERTIES_R : Node n = (Node)sendCmdRequest.element; SwingUtilities.invokeLater(new CommandExecutor.SetProperties( n, (String)sendCmdRequest.cmd.getArgAt(1), reply.getSource())); break; case CLEAR_PROPERTIES_R : SwingUtilities.invokeLater(new CommandExecutor.ClearProperties((Node)sendCmdRequest.element,reply.getSource())); break; case SET_NOTES_R : SwingUtilities.invokeLater(new CommandExecutor.SetNotes( diagram.getTreeModel(), ((SendTreeCmdRequest)sendCmdRequest).treeNode, (String)sendCmdRequest.cmd.getArgAt(sendCmdRequest.cmd.getArgNum()-1), reply.getSource())); break; case ADD_PROPERTY_R : SwingUtilities.invokeLater(new CommandExecutor.AddProperty( (Node)sendCmdRequest.element, (String)sendCmdRequest.cmd.getArgAt(1), (String)sendCmdRequest.cmd.getArgAt(2), reply.getSource())); break; case REMOVE_PROPERTY_R : SwingUtilities.invokeLater(new CommandExecutor.RemoveProperty( (Node)sendCmdRequest.element, (String)sendCmdRequest.cmd.getArgAt(1), (Integer)sendCmdRequest.cmd.getArgAt(2), reply.getSource())); break; case SET_MODIFIERS_R : indexes = new HashSet<Integer>(sendCmdRequest.cmd.getArgNum()-3); for(int i=3;i<sendCmdRequest.cmd.getArgNum();i++){ indexes.add((Integer)sendCmdRequest.cmd.getArgAt(i)); } SwingUtilities.invokeLater(new CommandExecutor.SetModifiers( (Node)sendCmdRequest.element, (String)sendCmdRequest.cmd.getArgAt(1), (Integer)sendCmdRequest.cmd.getArgAt(2), indexes, reply.getSource())); break; case SET_ENDLABEL_R : synchronized(diagram.getCollectionModel().getMonitor()){ node = Finder.findNode((Long)sendCmdRequest.cmd.getArgAt(1),diagram.getCollectionModel().getNodes()); } SwingUtilities.invokeLater(new CommandExecutor.SetEndLabel( (Edge)sendCmdRequest.element, node, (String)sendCmdRequest.cmd.getArgAt(2), reply.getSource())); break; case SET_ENDDESCRIPTION_R : synchronized(diagram.getCollectionModel().getMonitor()){ node = Finder.findNode((Long)sendCmdRequest.cmd.getArgAt(1),diagram.getCollectionModel().getNodes()); } SwingUtilities.invokeLater(new CommandExecutor.SetEndDescription( (Edge)sendCmdRequest.element, node, /* if the endDescription ain't included then we have to set it to null ( = NONE )*/ sendCmdRequest.cmd.getArgNum() == 3 ? (Integer)sendCmdRequest.cmd.getArgAt(2): -1, reply.getSource())); break; case TRANSLATE_NODE_R : node = (Node)sendCmdRequest.element; SwingUtilities.invokeLater(new CommandExecutor.Translate( node, new Point2D.Double((Double)sendCmdRequest.cmd.getArgAt(1),(Double)sendCmdRequest.cmd.getArgAt(2)), (Double)sendCmdRequest.cmd.getArgAt(3), (Double)sendCmdRequest.cmd.getArgAt(4), reply.getSource())); break; case TRANSLATE_EDGE_R : edge = (Edge)sendCmdRequest.element; SwingUtilities.invokeLater(new CommandExecutor.Translate( edge, new Point2D.Double((Double)sendCmdRequest.cmd.getArgAt(1),(Double)sendCmdRequest.cmd.getArgAt(2)), (Double)sendCmdRequest.cmd.getArgAt(3), (Double)sendCmdRequest.cmd.getArgAt(4), reply.getSource() )); break; case BEND_R : edge = (Edge)sendCmdRequest.element; Point2D bendStart = null; if(sendCmdRequest.cmd.getArgNum() == 5){ bendStart = new Point2D.Double((Double)sendCmdRequest.cmd.getArgAt(3),(Double)sendCmdRequest.cmd.getArgAt(4)); } SwingUtilities.invokeLater(new CommandExecutor.Bend( edge, new Point2D.Double((Double)sendCmdRequest.cmd.getArgAt(1),(Double)sendCmdRequest.cmd.getArgAt(2)), bendStart, reply.getSource() )); break; case STOP_EDGE_MOVE_R : edge = (Edge)sendCmdRequest.element; SwingUtilities.invokeLater(new CommandExecutor.StopMove(edge,reply.getSource())); break; case STOP_NODE_MOVE_R : node = (Node)sendCmdRequest.element; SwingUtilities.invokeLater(new CommandExecutor.StopMove(node,reply.getSource())); break; case ERROR_R : SwingUtilities.invokeLater(new CommandExecutor.ShowErrorMessageDialog(tabbedPane, "Error for command on "+ sendCmdRequest.element.getName()+ ". " +reply.getMessage(),reply.getSource())); InteractionLog.log("SERVER", "error:reply from server", DiagramElement.toLogString(sendCmdRequest.element) + " " +reply.getMessage()); break; default : throw new RuntimeException("Reply message not recognized: "+reply.getName()); } }else if(msg instanceof LockMessage){ // lock message from the server try { answers.put(new LockAnswer((LockMessage)msg)); } catch (InterruptedException e) { throw new RuntimeException(e); // must never happen } }else{ // awareness message AwarenessMessage awMsg = (AwarenessMessage)msg; DisplayFilter filter = DisplayFilter.getInstance(); if(filter != null){// if awareness panel ain't open, drop the packet switch(awMsg.getName()){ case START_A :{ if(filter.configurationHasChanged()){ for(Diagram d : channels.values()) getAwarenessPanelEditor().clearRecords(d.getName()); } DiagramEventActionSource actionSource = (DiagramEventActionSource)awMsg.getSource(); if(actionSource.getCmd() == Command.Name.INSERT_NODE || actionSource.getCmd() == Command.Name.INSERT_EDGE || actionSource.getCmd() == Command.Name.SELECT_NODE_FOR_EDGE_CREATION) getAwarenessPanelEditor().addTimedRecord(diagramName, filter.processForText(actionSource)); else getAwarenessPanelEditor().addRecord(diagramName, filter.processForText(actionSource)); /* announce the just received awareness message via the second voice */ NarratorFactory.getInstance().speakWholeText(filter.processForSpeech(actionSource), Narrator.SECOND_VOICE); }break; case STOP_A : { if(filter.configurationHasChanged()){ for(Diagram d : channels.values()) getAwarenessPanelEditor().clearRecords(d.getName()); } DiagramEventActionSource actionSource = (DiagramEventActionSource)awMsg.getSource(); /* unselect node for edge creation is announced and put temporarily on the text panel * * (select node for edge creation is temporary as well so we don't need to clean the panel) */ if(actionSource.getCmd() == Command.Name.UNSELECT_NODE_FOR_EDGE_CREATION){ getAwarenessPanelEditor().addTimedRecord(diagramName, filter.processForText(actionSource)); NarratorFactory.getInstance().speakWholeText(filter.processForSpeech(actionSource)); }else{ getAwarenessPanelEditor().removeRecord(diagramName,filter.processForText(actionSource)); } }break; case USERNAME_A : { String userNames = (String)awMsg.getSource(); getAwarenessPanelEditor().replaceUserName(diagramName,userNames); }break; case ERROR_A : { SpeechOptionPane.showMessageDialog(tabbedPane, (String)awMsg.getSource()); }break; } } } } } } /* this part is never reached out unless the thread is shut down */ for(SocketChannel channel : channels.keySet()){ try{channel.close();}catch(IOException ioe){ioe.printStackTrace();} } } public void shutdown(){ mustSayGoodbye = true; selector.wakeup(); } private void handleRequests() { while(!requests.isEmpty()){ Request request = requests.poll(); if(request instanceof AddDiagramRequest){ AddDiagramRequest adr = (AddDiagramRequest)request; try { adr.channel.configureBlocking(false); adr.channel.register(selector, SelectionKey.OP_READ); channels.put(adr.channel, adr.diagram); } catch (IOException ioe) { /* something went wrong, turn the diagram back into a local one */ /* put the entry in channels just for a moment as it will be used in revertDiagram */ channels.put(adr.channel, adr.diagram); revertDiagram(adr.channel,false); } }else if(request instanceof RmDiagramRequest){ // user un-shared a diagram RmDiagramRequest rdr = (RmDiagramRequest)request; Set<Map.Entry<SocketChannel, Diagram>> entryset = channels.entrySet(); SocketChannel channel = null; for(Map.Entry<SocketChannel, Diagram> entry : entryset){ if(entry.getValue().getName().equals(rdr.diagramName)){ channel = entry.getKey(); } } if(channel != null) revertDiagram(channel,true); }else if(request instanceof SendCmdRequest||request instanceof SendTreeCmdRequest){ SendCmdRequest scr = (SendCmdRequest)request; //System.out.println("ClientConnectionManager:handling request "+scr.cmd.getName()); if(!channels.containsKey(scr.channel)) continue; // commands issued after reverting a diagram are dropped pendingCommands.add(scr); try{ protocol.send(scr.channel, scr.cmd); }catch(IOException e){ /* the pending commands is normally removed upon reply receive */ pendingCommands.remove(scr); revertDiagram(scr.channel,false); } }else if(request instanceof SendLockRequest){ SendLockRequest slr = (SendLockRequest)request; try { protocol.send(slr.channel,slr.lock); } catch (IOException e) { revertDiagram(slr.channel,false); try { /* RevertedDiagramAnswer is to prevent the Event dispatching Thread from blocking if the * * server goes down and the client is still waiting for an answer. If the thread * * was not waiting for an answers the RevertedDiagramAnswer will not be put in the queue */ if(waitingAnswer) answers.put(new RevertedDiagramAnswer()); } catch (InterruptedException ie) { throw new RuntimeException(ie); //must never happen } } }else if(request instanceof SendAwarenessRequest){ SendAwarenessRequest awr = (SendAwarenessRequest)request; try{ protocol.send(awr.channel, awr.awMsg); }catch (IOException e) { revertDiagram(awr.channel,false); } } } } private void revertDiagram(SocketChannel c,final boolean userRequest){ /* from now on all the commands using this channel will be dropped */ final Diagram diagram = channels.remove(c); if(diagram == null) return; try{c.close();}catch(IOException ioe){ioe.printStackTrace();} SwingUtilities.invokeLater(new Runnable(){ @Override public void run() { for(int i=0; i< tabbedPane.getTabCount();i++){ DiagramPanel dPanel = (DiagramPanel)tabbedPane.getComponentAt(i); if(dPanel.getDiagram() instanceof NetDiagram){ NetDiagram netDiagram = (NetDiagram)dPanel.getDiagram(); if( netDiagram.getDelegate().equals(diagram)){ /* set the old (unwrapped) diagram as the current one */ dPanel.setAwarenessPanelEnabled(false); dPanel.setDiagram(diagram); break; } } } if(!userRequest)// show the message only if the revert is due to an error SpeechOptionPane.showMessageDialog(tabbedPane, MessageFormat.format( ResourceBundle.getBundle(Server.class.getName()).getString("dialog.error.connection"), diagram.getName()) ); } }); } private void revertAllDiagrams(){ for(SocketChannel c : channels.keySet()) try{c.close();}catch(IOException ioe){ioe.printStackTrace();} channels.clear(); SwingUtilities.invokeLater(new Runnable(){ @Override public void run() { for(int i=0; i< tabbedPane.getTabCount();i++){ DiagramPanel dPanel = (DiagramPanel)tabbedPane.getComponentAt(i); if(dPanel.getDiagram() instanceof NetDiagram){ NetDiagram netDiagram = (NetDiagram)dPanel.getDiagram(); /* set the old (unwrapped) diagram as the current one */ dPanel.setAwarenessPanelEnabled(false); dPanel.setDiagram(netDiagram.getDelegate()); } } SpeechOptionPane.showMessageDialog( tabbedPane, ResourceBundle.getBundle(Server.class.getName()).getString("dialog.error.connections")); } }); } public interface Request {}; public interface DiagramRequest extends Request {}; public static class AddDiagramRequest implements DiagramRequest { public AddDiagramRequest(SocketChannel channel, Diagram diagram){ this.channel = channel; this.diagram = diagram; } public SocketChannel channel; public Diagram diagram; } public static class RmDiagramRequest implements DiagramRequest { public RmDiagramRequest(String diagramName){ this.diagramName = diagramName; } public String diagramName; } public static class SendCmdRequest implements Request { public SendCmdRequest(Command cmd, SocketChannel channel, DiagramElement element ){ this.cmd = cmd; this.element = element;this.channel = channel; } public boolean matches(SocketChannel c,String diagramName){ return(diagramName.equals(cmd.getDiagram())&&c.socket().getInetAddress().equals(channel.socket().getInetAddress())); } public DiagramElement element; public SocketChannel channel; public Command cmd; } public static class SendTreeCmdRequest extends SendCmdRequest{ public SendTreeCmdRequest( Command cmd,SocketChannel channel,DiagramTreeNode treeNode) { super(cmd,channel,null); this.treeNode = treeNode; } public DiagramTreeNode treeNode; public SocketChannel channel; public Command cmd; } public static class SendLockRequest implements Request { public SendLockRequest (SocketChannel channel, LockMessage lock){ this.channel = channel; this.lock = lock; } public SocketChannel channel; public LockMessage lock; } public static class SendAwarenessRequest implements Request { public SendAwarenessRequest(SocketChannel channel, AwarenessMessage awMsg){ this.awMsg = awMsg; this.channel = channel; } public SocketChannel channel; public AwarenessMessage awMsg; } public interface Answer {}; public static class LockAnswer implements Answer { public LockAnswer(LockMessage answer){ this.message = answer; } public LockMessage message; } public static class RevertedDiagramAnswer implements Answer{} private Node node; private Edge edge; private DiagramTreeNode treeNode; private Set<Integer> indexes; private SendCmdRequest sendCmdRequest; /* for each server hold the diagram it shares with it */ private Map<SocketChannel, Diagram> channels; private ConcurrentLinkedQueue<Request> requests; private BlockingQueue<Answer> answers; private LinkedList<SendCmdRequest> pendingCommands; private Selector selector; private EditorTabbedPane tabbedPane; private Protocol protocol; private volatile boolean waitingAnswer; private volatile boolean mustSayGoodbye; }