1 // ******************************************************************************
2 //
3 // Title: Force Field X.
4 // Description: Force Field X - Software for Molecular Biophysics.
5 // Copyright: Copyright (c) Michael J. Schnieders 2001-2025.
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.InputStream;
41 import java.io.ObjectInputStream;
42 import java.io.ObjectOutputStream;
43 import java.io.OutputStream;
44 import java.net.InetSocketAddress;
45 import java.net.Socket;
46 import java.util.logging.Logger;
47
48 /**
49 * The FFXClient class encapsulates a socket connection to an FFXServer started by an executing FFX
50 * instance. FFXSystem and FFXUpdate objects are sent by the FFXServer to the FFXClient on request.
51 *
52 * @author Michael J. Schnieders
53 */
54 public class FFXClient {
55
56 private static final Logger logger = Logger.getLogger(FFXClient.class.getName());
57 private Socket client; // Socket connection to the server
58 private InetSocketAddress address; // Server address
59 private InputStream in; // Input from the server
60 private ObjectInputStream oin; // Input form the server
61 private OutputStream out; // Output to the server
62 private ObjectOutputStream oout; // Output to the server
63 private SimulationDefinition system; // Tinker system definition
64 private SimulationUpdate update; // Tinker update information
65 private SimulationMessage message; // Message Passed Between Client & Server
66 // Various connection status variables
67 private int retryCount = 0; // Count of Client attempts to Connect
68 private int retryLimit = 10000; // Maximum number of retries
69 private boolean connectionMade = false; // True when a connection has been
70 // made
71 // closed is True if the server closes an open connection
72 // or if the retryLimit is reached
73 private boolean closed = false;
74
75 /** Constructor for FFXClient. */
76 public FFXClient() {
77 address = new InetSocketAddress(2000);
78 }
79
80 /**
81 * Constructor for FFXClient.
82 *
83 * @param a a {@link java.net.InetSocketAddress} object.
84 */
85 public FFXClient(InetSocketAddress a) {
86 address = a;
87 }
88
89 /**
90 * Constructor for FFXClient.
91 *
92 * @param port a int.
93 */
94 public FFXClient(int port) {
95 address = new InetSocketAddress(port);
96 }
97
98 /**
99 * Attempts to connect to a Tinker FServer. If this FClient is already connected, the connection
100 * will be closed.
101 */
102 public void connect() {
103 if (client != null && client.isConnected()) {
104 release();
105 }
106 closed = false;
107 client = new Socket();
108 try {
109 client.connect(address, 100);
110 client.setTcpNoDelay(true);
111 out = client.getOutputStream();
112 oout = new ObjectOutputStream(out);
113 in = client.getInputStream();
114 oin = new ObjectInputStream(in);
115 connectionMade = true;
116 logger.info("Connected to FFX Server: " + client);
117 } catch (Exception e) {
118 client = null;
119 } finally {
120 if (client == null || !client.isConnected()) {
121 release();
122 }
123 }
124 }
125
126 /**
127 * Getter for the field <code>system</code>.
128 *
129 * @return a {@link ffx.ui.commands.SimulationDefinition} object.
130 */
131 public SimulationDefinition getSystem() {
132 readSocket();
133 return system;
134 }
135
136 /**
137 * Getter for the field <code>update</code>.
138 *
139 * @return a {@link ffx.ui.commands.SimulationUpdate} object.
140 */
141 public SimulationUpdate getUpdate() {
142 readSocket();
143 return update;
144 }
145
146 /**
147 * isClosed
148 *
149 * @return a boolean.
150 */
151 public boolean isClosed() {
152 return closed;
153 }
154
155 /**
156 * isConnected
157 *
158 * @return a boolean.
159 */
160 public boolean isConnected() {
161 if (client != null && client.isConnected()) {
162 return true;
163 }
164 return false;
165 }
166
167 /** readSocket */
168 public void readSocket() {
169 try {
170 while (oin != null && in.available() > 0) {
171 Object o = oin.readObject();
172 if (o instanceof SimulationMessage) {
173 message = (SimulationMessage) o;
174 // logger.info(message.toString());
175 if (message.getMessage() == SimulationMessage.SYSTEM) {
176 system = (SimulationDefinition) oin.readObject();
177 system.read = false;
178 } else if (message.getMessage() == SimulationMessage.UPDATE) {
179 update = (SimulationUpdate) oin.readObject();
180 update.read = false;
181 } else if (message.getMessage() == SimulationMessage.CLOSING) {
182 closed = true;
183 release();
184 }
185 }
186 }
187 if (system == null) {
188 message = new SimulationMessage(SimulationMessage.SYSTEM);
189 oout.reset();
190 oout.writeObject(message);
191 oout.flush();
192 } else if (update == null || update.read) {
193 message = new SimulationMessage(SimulationMessage.UPDATE);
194 if (update != null) {
195 if (update.type == SimulationUpdate.SIMULATION) {
196 message.setTime(update.time);
197 } else {
198 message.setStep(update.step);
199 }
200 }
201 oout.reset();
202 oout.writeObject(message);
203 oout.flush();
204 }
205 } catch (Exception e) {
206 logger.warning("Exception reading data from FFX\n" + e);
207 release();
208 }
209 }
210
211 /** release */
212 public void release() {
213 if (client == null) {
214 return;
215 }
216 retryCount++;
217 if (retryCount > retryLimit || connectionMade) {
218 closed = true;
219 }
220 if (client != null && client.isConnected() && oout != null) {
221 try {
222 SimulationMessage close = new SimulationMessage(SimulationMessage.CLOSING);
223 oout.reset();
224 oout.writeObject(close);
225 oout.flush();
226 } catch (Exception e) {
227 oout = null;
228 }
229 }
230 try {
231 if (oin != null) {
232 oin.close();
233 }
234 if (in != null) {
235 in.close();
236 }
237 if (oout != null) {
238 oout.close();
239 }
240 if (out != null) {
241 out.close();
242 }
243 if (client != null) {
244 client.close();
245 }
246 } catch (Exception e) {
247 client = null;
248 } finally {
249 in = null;
250 oin = null;
251 out = null;
252 oout = null;
253 client = null;
254 }
255 }
256 }