1 package com.sparshui.server; 2 3 import java.io.IOException; 4 import java.net.ServerSocket; 5 import java.net.Socket; 6 import java.util.Map; 7 8 import org.jmol.api.JmolGestureServerInterface; 9 import javajs.util.Lst; 10 import org.jmol.util.Logger; 11 12 import com.sparshui.common.ConnectionType; 13 import com.sparshui.common.Location; 14 import com.sparshui.common.NetworkConfiguration; 15 import com.sparshui.common.messages.events.EventType; 16 17 /** 18 * The main gesture server class. In the Jmol version, this server is created by 19 * org.jmol.multitouch.sparshui.JmolSparshClientAdapter so there is no main 20 * method. 21 * 22 * adapted by Bob Hanson for Jmol 11/29/2009 23 * 24 * @author Tony Ross 25 * 26 */ 27 28 public class GestureServer implements Runnable, JmolGestureServerInterface { 29 GestureServer clientServer, deviceServer, main; 30 Thread clientThread, deviceThread; 31 ServerSocket _clientSocket; 32 ServerSocket _deviceSocket; 33 ServerSocket _mySocket; 34 private Lst<ClientConnection> _clients = new Lst<ClientConnection>(); 35 private int port; 36 37 InputDeviceConnection ic = null; 38 GestureServer()39 public GestureServer() { 40 // for reflection 41 Logger.info(this + " constructed"); 42 } 43 44 @Override finalize()45 public void finalize() { 46 if (Logger.debugging) 47 Logger.debug(this + " finalized"); 48 } 49 GestureServer(int port, GestureServer main)50 public GestureServer(int port, GestureServer main) { 51 this.port = port; 52 this.main = main; 53 } 54 55 /** 56 * two independent threads -- one accepting multiple clients, one not. 57 */ 58 @Override startGestureServer()59 public void startGestureServer() { 60 clientServer = new GestureServer(NetworkConfiguration.CLIENT_PORT, this); 61 clientThread = new Thread(clientServer); 62 clientThread.setName("Jmol SparshUI Client GestureServer on port " 63 + NetworkConfiguration.CLIENT_PORT); 64 clientThread.start(); 65 deviceServer = new GestureServer(NetworkConfiguration.DEVICE_PORT, this); 66 deviceThread = new Thread(deviceServer); 67 deviceThread.setName("Jmol SparshUI Device GestureServer on port " 68 + NetworkConfiguration.DEVICE_PORT); 69 deviceThread.start(); 70 } 71 72 @Override dispose()73 public void dispose() { 74 try { 75 _clientSocket.close(); 76 } catch (Exception e) { 77 // ignore 78 } 79 try { 80 _deviceSocket.close(); 81 } catch (Exception e) { 82 // ignore 83 } 84 try { 85 clientThread.interrupt(); 86 } catch (Exception e) { 87 // ignore 88 } 89 try { 90 deviceThread.interrupt(); 91 } catch (Exception e) { 92 // ignore 93 } 94 _clientSocket = null; 95 clientThread = null; 96 _deviceSocket = null; 97 deviceThread = null; 98 clientServer = null; 99 deviceServer = null; 100 } 101 102 /** 103 * Start accepting connections. 104 */ 105 @Override run()106 public void run() { 107 try { 108 openSocket(); 109 acceptConnections(); 110 } catch (Exception e) { 111 Logger.info("[GestureServer] connection unavailable"); 112 } 113 } 114 115 /** 116 * 117 */ openSocket()118 private void openSocket() { 119 try { 120 if (port == NetworkConfiguration.CLIENT_PORT) 121 _mySocket = main._clientSocket = new ServerSocket(port); 122 else 123 _mySocket = main._deviceSocket = new ServerSocket(port); 124 Logger.info("[GestureServer] Socket Open: " + port); 125 main.myState = JmolGestureServerInterface.OK; 126 } catch (IOException e) { 127 Logger.error("[GestureServer] Failed to open a server socket."); 128 e.printStackTrace(); 129 main.myState = 0; 130 } 131 } 132 133 /** 134 * 135 */ acceptConnections()136 private void acceptConnections() { 137 while (!_mySocket.isClosed()) { 138 try { 139 if (port == NetworkConfiguration.DEVICE_PORT) { 140 Logger.info("[GestureServer] Accepting device connections"); 141 acceptConnection(_mySocket.accept()); 142 return; // only one of these 143 } 144 Logger.info("[GestureServer] Accepting client connections"); 145 acceptConnection(_mySocket.accept()); 146 } catch (IOException e) { 147 Logger.error("[GestureServer] Failed to establish connection on port " + port); 148 e.printStackTrace(); 149 } 150 } 151 Logger.info("[GestureServer] Socket Closed on port " + port); 152 } 153 154 /** 155 * 156 * @param socket 157 * @throws IOException 158 */ acceptConnection(Socket socket)159 private void acceptConnection(Socket socket) throws IOException { 160 // no remote access! 161 byte[] add = socket.getInetAddress().getAddress(); 162 if (add[0] != 127 || add[1] != 0 || add[2] != 0 || add[3] != 1) 163 return; 164 int type = socket.getInputStream().read(); 165 if (type == ConnectionType.CLIENT) { 166 Logger.info("[GestureServer] client connection established on port " + port); 167 acceptClientConnection(socket); 168 } else if (type == ConnectionType.INPUT_DEVICE) { 169 Logger.info("[GestureServer] device connection established on port " + port); 170 acceptInputDeviceConnection(socket); 171 } 172 } 173 174 /** 175 * 176 * @param socket 177 * @throws IOException 178 */ acceptClientConnection(Socket socket)179 private void acceptClientConnection(Socket socket) throws IOException { 180 Logger.info("[GestureServer] Client connection accepted"); 181 ClientConnection cc = new ClientConnection(socket); 182 main._clients.addLast(cc); 183 if (main.ic == null) { 184 cc.processError(EventType.DRIVER_NONE); 185 } else { 186 main.myState |= JmolGestureServerInterface.HAS_CLIENT; 187 } 188 } 189 190 /** 191 * 192 * @param socket 193 * @throws IOException 194 */ acceptInputDeviceConnection(Socket socket)195 private void acceptInputDeviceConnection(Socket socket) throws IOException { 196 Logger.info("[GestureServer] Input device connection accepted"); 197 main.ic = new InputDeviceConnection(this, socket); 198 main.myState |= JmolGestureServerInterface.HAS_DRIVER; 199 } 200 201 /** 202 * 203 * notify clients that we lost contact with the input device 204 * 205 */ notifyInputLost()206 void notifyInputLost() { 207 Logger 208 .error("[GestureServer] sending clients message that input device was lost."); 209 main.ic = null; 210 main.myState &= ~JmolGestureServerInterface.HAS_DRIVER; 211 processBirth(null); 212 } 213 214 /** 215 * This method was tucked into InputDeviceConnection but really has to do more 216 * with server-to-client interaction, so I moved it here. BH 217 * 218 * @param inputDeviceTouchPoints 219 * container for this input device's touchPoints 220 * @param id 221 * @param location 222 * @param time 223 * @param state 224 * @return whether a client has claimed this touchPoint; 225 */ processTouchPoint(Map<Integer, TouchPoint> inputDeviceTouchPoints, int id, Location location, long time, int state)226 boolean processTouchPoint(Map<Integer, TouchPoint> inputDeviceTouchPoints, int id, 227 Location location, long time, int state) { 228 if (Logger.debugging) { 229 Logger.debug("[GestureServer] processTouchPoint id=" + id + " state=" + state + " " + location 230 + " " + time); 231 } 232 Integer iid = Integer.valueOf(id); 233 if (inputDeviceTouchPoints.containsKey(iid)) { 234 TouchPoint touchPoint = inputDeviceTouchPoints.get(iid); 235 if (!touchPoint.isClaimed()) 236 return false; 237 if (Logger.debugging) 238 Logger.debug("[GestureServer] OK"); 239 synchronized (touchPoint) { 240 touchPoint.update(location, time, state); 241 } 242 return true; 243 } 244 TouchPoint touchPoint = new TouchPoint(id, location, time); 245 inputDeviceTouchPoints.put(iid, touchPoint); 246 return processBirth(touchPoint); 247 } 248 249 /** 250 * Process a touch point birth by getting the groupID and gestures for the 251 * touch point. NULL touchpoint means we have a driver failure 252 * 253 * @param touchPoint 254 * The new touch point. 255 * @return whether a client has claimed this touchPoint as its own. 256 * 257 */ processBirth(TouchPoint touchPoint)258 private boolean processBirth(TouchPoint touchPoint) { 259 Lst<ClientConnection> clients_to_remove = null; 260 boolean isClaimed = false; 261 for (int i = 0; i < main._clients.size(); i++) { 262 ClientConnection client = main._clients.get(i); 263 // Return if the client claims the touch point 264 try { 265 if (touchPoint == null) 266 client.processError(EventType.DRIVER_NONE); 267 else 268 isClaimed = client.processBirth(touchPoint); 269 if (isClaimed) 270 break; 271 } catch (IOException e) { 272 // This occurs if there is a communication error 273 // with the client. In this case, we will want 274 // to remove the client. 275 if (clients_to_remove == null) 276 clients_to_remove = new Lst<ClientConnection>(); 277 clients_to_remove.addLast(client); 278 } 279 } 280 if (clients_to_remove != null) 281 for (int i = 0; i < clients_to_remove.size(); i++) { 282 main._clients.removeObj(clients_to_remove.get(i)); 283 Logger.info("[GestureServer] Client Disconnected"); 284 } 285 return isClaimed; 286 } 287 288 private int myState; 289 @Override getState()290 public int getState() { 291 return myState; 292 } 293 294 } 295