View Javadoc
1   // ******************************************************************************
2   //
3   // Title:       Force Field X.
4   // Description: Force Field X - Software for Molecular Biophysics.
5   // Copyright:   Copyright (c) Michael J. Schnieders 2001-2024.
6   //
7   // This file is part of Force Field X.
8   //
9   // Force Field X is free software; you can redistribute it and/or modify it
10  // under the terms of the GNU General Public License version 3 as published by
11  // the Free Software Foundation.
12  //
13  // Force Field X is distributed in the hope that it will be useful, but WITHOUT
14  // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15  // FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
16  // details.
17  //
18  // You should have received a copy of the GNU General Public License along with
19  // Force Field X; if not, write to the Free Software Foundation, Inc., 59 Temple
20  // Place, Suite 330, Boston, MA 02111-1307 USA
21  //
22  // Linking this library statically or dynamically with other modules is making a
23  // combined work based on this library. Thus, the terms and conditions of the
24  // GNU General Public License cover the whole combination.
25  //
26  // As a special exception, the copyright holders of this library give you
27  // permission to link this library with independent modules to produce an
28  // executable, regardless of the license terms of these independent modules, and
29  // to copy and distribute the resulting executable under terms of your choice,
30  // provided that you also meet, for each linked independent module, the terms
31  // and conditions of the license of that module. An independent module is a
32  // module which is not derived from or based on this library. If you modify this
33  // library, you may extend this exception to your version of the library, but
34  // you are not obligated to do so. If you do not wish to do so, delete this
35  // exception statement from your version.
36  //
37  // ******************************************************************************
38  package ffx.ui.commands;
39  
40  import java.io.ObjectInputStream;
41  import java.io.ObjectOutputStream;
42  import java.net.InetAddress;
43  import java.net.InetSocketAddress;
44  import java.net.ServerSocket;
45  import java.net.Socket;
46  import java.util.ListIterator;
47  import java.util.Vector;
48  import java.util.logging.Logger;
49  
50  /**
51   * The FFXServer is launched by an FFX instance to allow Force Field X Clients to connect.
52   *
53   * @author Michael J. Schnieders
54   */
55  public class FFXServer implements Runnable {
56  
57    private static final Logger logger = Logger.getLogger(FFXServer.class.getName());
58    private static SimulationMessage closing = new SimulationMessage(SimulationMessage.CLOSING);
59    private ServerSocket server;
60    private int serverPort = 2000;
61    private int serverTimeout = 100;
62    private Thread thread;
63    private int sleepTime = 1;
64    private boolean init = true;
65    private int cycle = 0;
66    private boolean shutdown = false;
67    private boolean request = false;
68    private Vector<Socket> clients = new Vector<Socket>();
69    private Vector<ObjectOutputStream> outputs = new Vector<ObjectOutputStream>();
70    private Vector<ObjectInputStream> inputs = new Vector<ObjectInputStream>();
71    private SimulationDefinition system = null;
72    private SimulationUpdate update = null;
73  
74    /**
75     * Constructor for FFXServer.
76     *
77     * @param s a {@link ffx.ui.commands.SimulationDefinition} object.
78     */
79    public FFXServer(SimulationDefinition s) {
80      system = s;
81    }
82  
83    /**
84     * isAlive
85     *
86     * @return a boolean.
87     */
88    public boolean isAlive() {
89      if (thread == null) {
90        return false;
91      } else if (thread.isAlive()) {
92        return true;
93      } else {
94        return false;
95      }
96    }
97  
98    /**
99     * loadUpdate
100    *
101    * @param u a {@link ffx.ui.commands.SimulationUpdate} object.
102    */
103   public void loadUpdate(SimulationUpdate u) {
104     update = u;
105   }
106 
107   /**
108    * needUpdate
109    *
110    * @return a boolean.
111    */
112   public boolean needUpdate() {
113     if (clients.isEmpty()) {
114       sleepTime = 100;
115       return false;
116     }
117     if (request != true) {
118       return false;
119     }
120     sleepTime = 1;
121     return true;
122   }
123 
124   /** run */
125   public void run() {
126     startServer();
127     while (!shutdown) {
128       accept();
129       send();
130       try {
131         Thread.sleep(sleepTime);
132       } catch (Exception e) {
133         // What to do here ??
134         logger.severe("Server: thread sleep interrupted\n");
135       }
136       if (init) {
137         cycle++;
138         if (cycle >= 10) {
139           init = false;
140           serverTimeout = 1;
141         }
142       }
143     }
144     accept();
145     send();
146     closeServer();
147   }
148 
149   /** setUpdated */
150   public void setUpdated() {
151     request = false;
152   }
153 
154   /** start */
155   public void start() {
156     if (thread == null || !thread.isAlive()) {
157       thread = new Thread(this);
158       thread.setPriority(Thread.MAX_PRIORITY);
159       thread.start();
160     }
161   }
162 
163   /** stop */
164   public void stop() {
165     shutdown = true;
166   }
167 
168   private void accept() {
169     Socket client = null;
170     ObjectOutputStream oout = null;
171     ObjectInputStream oin = null;
172     if (server != null) {
173       try {
174         client = server.accept();
175         if (client != null && client.isConnected()) {
176           client.setTcpNoDelay(true);
177           clients.add(client);
178           oout = new ObjectOutputStream(client.getOutputStream());
179           outputs.add(oout);
180           oin = new ObjectInputStream(client.getInputStream());
181           inputs.add(oin);
182           Logger.getLogger("ffx").info("Client connected\n" + client);
183         }
184       } catch (Exception e) {
185         if (client != null) {
186           clients.remove(client);
187         }
188         if (oout != null) {
189           outputs.remove(oout);
190         }
191         if (oin != null) {
192           inputs.remove(oin);
193         }
194       }
195     }
196   }
197 
198   private void closeClient(int index) {
199     Socket client = clients.get(index);
200     ObjectOutputStream oout = outputs.get(index);
201     ObjectInputStream oin = inputs.get(index);
202     try {
203       oout.reset();
204       oout.writeObject(closing);
205       oout.flush();
206     } catch (Exception e) {
207       try {
208         outputs.remove(index);
209         inputs.remove(index);
210         clients.remove(index);
211         if (oout != null) {
212           oout.close();
213         }
214         if (oin != null) {
215           oin.close();
216         }
217         if (client != null) {
218           client.close();
219         }
220       } catch (Exception ignored) {
221       }
222     }
223   }
224 
225   private void closeServer() {
226     for (int i = 0; i < clients.size(); i++) {
227       lastUpdate(i);
228     }
229     while (!clients.isEmpty()) {
230       closeClient(0);
231       try {
232         synchronized (this) {
233           wait(10);
234         }
235       } catch (Exception e) {
236         Logger.getLogger("ffx").severe(e.toString());
237       }
238     }
239     try {
240       if (server != null) {
241         server.close();
242         server = null;
243       }
244     } catch (Exception ignored) {
245     }
246   }
247 
248   private void lastUpdate(int index) {
249     try {
250       ObjectOutputStream oout = outputs.get(index);
251       Socket client = clients.get(index);
252       if (client != null && client.isConnected() && oout != null) {
253         SimulationMessage last = new SimulationMessage(SimulationMessage.SYSTEM);
254         if (system != null) {
255           oout.reset();
256           oout.writeObject(last);
257           oout.writeObject(system);
258           oout.flush();
259         }
260         if (update != null) {
261           oout.reset();
262           last.setMessage(SimulationMessage.UPDATE);
263           oout.writeObject(last);
264           oout.writeObject(update);
265           oout.flush();
266         }
267         last.setMessage(SimulationMessage.CLOSING);
268         oout.reset();
269         oout.writeObject(last);
270         oout.flush();
271       }
272     } catch (Exception e) {
273       logger.severe("" + e);
274     }
275   }
276 
277   private void send() {
278     if (system == null) {
279       return;
280     }
281     if (clients.isEmpty()) {
282       return;
283     }
284     ObjectOutputStream oout;
285     ObjectInputStream oin;
286     Socket client;
287     ListIterator<ObjectOutputStream> lout;
288     ListIterator<ObjectInputStream> lin;
289     ListIterator<Socket> lclient;
290     Vector<Socket> closed = new Vector<Socket>();
291     for (lout = outputs.listIterator(),
292             lin = inputs.listIterator(),
293             lclient = clients.listIterator();
294         lout.hasNext(); ) {
295       oout = lout.next();
296       oin = lin.next();
297       client = lclient.next();
298       if (!client.isConnected() || client.isClosed()) {
299         closed.add(client);
300       } else {
301         try {
302           SimulationMessage message = null;
303           while (oin != null && client.getInputStream().available() > 0) {
304             Object o = oin.readObject();
305             if (o instanceof SimulationMessage) {
306               message = (SimulationMessage) o;
307               if (message.getMessage() == SimulationMessage.CLOSING) {
308                 closed.add(client);
309                 message = null;
310                 break;
311               }
312             }
313           }
314           if (message != null) {
315             if (message.getMessage() == SimulationMessage.SYSTEM) {
316               synchronized (system) {
317                 oout.reset();
318                 oout.writeObject(message);
319                 oout.writeObject(system);
320                 oout.flush();
321               }
322             } else if (message.getMessage() == SimulationMessage.UPDATE) {
323               request = true;
324               if (update != null && update.isNewer(message)) {
325                 synchronized (update) {
326                   // logger.info("Sending Update");
327                   oout.reset();
328                   oout.writeObject(message);
329                   oout.writeObject(update);
330                   oout.flush();
331                 }
332               }
333             }
334           }
335         } catch (Exception e) {
336           closed.add(client);
337         }
338       }
339     }
340     for (Socket s : closed) {
341       int index = closed.indexOf(s);
342       closeClient(index);
343     }
344   }
345 
346   private void startServer() {
347     try {
348       server = new ServerSocket();
349       server.setSoTimeout(serverTimeout);
350       server.setReuseAddress(true);
351       server.bind(new InetSocketAddress(InetAddress.getLocalHost(), serverPort));
352     } catch (Exception e) {
353       try {
354         server.bind(new InetSocketAddress(InetAddress.getByName(null), serverPort));
355       } catch (Exception ex) {
356         Logger.getLogger("ffx").severe("SERVER -- Could not start\n" + e);
357         return;
358       }
359     }
360     Logger.getLogger("ffx").info("FFX Server Address: " + server.getLocalSocketAddress());
361     // JOptionPane.showMessageDialog(null, server.getLocalSocketAddress(),
362     // "Server Address", JOptionPane.ERROR_MESSAGE);
363   }
364 }