view java/src/uk/ac/qmul/eecs/ccmi/network/ClientConnectionManager.java @ 0:78b7fc5391a2

first import, outcome of NIME 2014 hackaton
author Fiore Martin <f.martin@qmul.ac.uk>
date Tue, 08 Jul 2014 16:28:59 +0100
parents
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;
}