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