52
|
1 package com.glavsoft.viewer.swing.gui;
|
|
2
|
|
3 import com.glavsoft.rfb.protocol.ProtocolSettings;
|
|
4 import com.glavsoft.utils.Strings;
|
|
5 import com.glavsoft.viewer.mvp.Model;
|
|
6 import com.glavsoft.viewer.swing.ConnectionParams;
|
|
7 import com.glavsoft.viewer.UiSettings;
|
|
8 import com.glavsoft.viewer.UiSettingsData;
|
|
9
|
|
10 import java.io.*;
|
|
11 import java.security.AccessControlException;
|
|
12 import java.util.*;
|
|
13 import java.util.logging.Logger;
|
|
14 import java.util.prefs.BackingStoreException;
|
|
15 import java.util.prefs.Preferences;
|
|
16
|
|
17 /**
|
|
18 * @author dime at tightvnc.com
|
|
19 */
|
|
20 public class ConnectionsHistory implements Model {
|
|
21 private static int MAX_ITEMS = 32;
|
|
22 public static final String CONNECTIONS_HISTORY_ROOT_NODE = "com/glavsoft/viewer/connectionsHistory";
|
|
23 public static final String NODE_HOST_NAME = "hostName";
|
|
24 public static final String NODE_PORT_NUMBER = "portNumber";
|
|
25 public static final String NODE_SSH_USER_NAME = "sshUserName";
|
|
26 public static final String NODE_SSH_HOST_NAME = "sshHostName";
|
|
27 public static final String NODE_SSH_PORT_NUMBER = "sshPortNumber";
|
|
28 public static final String NODE_USE_SSH = "useSsh";
|
|
29 public static final String NODE_PROTOCOL_SETTINGS = "protocolSettings";
|
|
30 public static final String NODE_UI_SETTINGS = "uiSettings";
|
|
31 private final Logger logger;
|
|
32
|
|
33 private Map<ConnectionParams, ProtocolSettings> protocolSettingsMap;
|
|
34 private Map<ConnectionParams, UiSettingsData> uiSettingsDataMap;
|
|
35 LinkedList<ConnectionParams> connections;
|
|
36
|
|
37 public ConnectionsHistory() {
|
|
38 logger = Logger.getLogger(getClass().getName());
|
|
39 init();
|
|
40 retrieve();
|
|
41 }
|
|
42
|
|
43 private void init() {
|
|
44 protocolSettingsMap = new HashMap<ConnectionParams, ProtocolSettings>();
|
|
45 uiSettingsDataMap = new HashMap<ConnectionParams, UiSettingsData>();
|
|
46 connections = new LinkedList<ConnectionParams>();
|
|
47 }
|
|
48
|
|
49 private void retrieve() {
|
|
50 Preferences connectionsHistoryNode;
|
|
51 try {
|
|
52 connectionsHistoryNode = getConnectionHistoryNode();
|
|
53 } catch (AccessControlException ace) {
|
|
54 return;
|
|
55 }
|
|
56 try {
|
|
57 final String[] orderNums;
|
|
58 orderNums = connectionsHistoryNode.childrenNames();
|
|
59 SortedMap<Integer, ConnectionParams> conns = new TreeMap<Integer, ConnectionParams>();
|
|
60 HashSet<ConnectionParams> uniques = new HashSet<ConnectionParams>();
|
|
61 for (String orderNum : orderNums) {
|
|
62 int num = 0;
|
|
63 try {
|
|
64 num = Integer.parseInt(orderNum);
|
|
65 } catch (NumberFormatException skip) {
|
|
66 //nop
|
|
67 }
|
|
68 Preferences node = connectionsHistoryNode.node(orderNum);
|
|
69 String hostName = node.get(NODE_HOST_NAME, null);
|
|
70 if (null == hostName) continue; // skip entries without hostName field
|
|
71 ConnectionParams cp = new ConnectionParams(hostName, node.getInt(NODE_PORT_NUMBER, 0),
|
|
72 node.getBoolean(NODE_USE_SSH, false), node.get(NODE_SSH_HOST_NAME, ""), node.getInt(NODE_SSH_PORT_NUMBER, 0), node.get(NODE_SSH_USER_NAME, "")
|
|
73 );
|
|
74 if (uniques.contains(cp)) continue; // skip duplicates
|
|
75 uniques.add(cp);
|
|
76 conns.put(num, cp);
|
|
77 logger.finest("deserialialize: " + cp.toPrint());
|
|
78 retrieveProtocolSettings(node, cp);
|
|
79 retrieveUiSettings(node, cp);
|
|
80 }
|
|
81 int itemsCount = 0;
|
|
82 for (ConnectionParams cp : conns.values()) {
|
|
83 if (itemsCount < MAX_ITEMS) {
|
|
84 connections.add(cp);
|
|
85 } else {
|
|
86 connectionsHistoryNode.node(cp.hostName).removeNode();
|
|
87 }
|
|
88 ++itemsCount;
|
|
89 }
|
|
90 } catch (BackingStoreException e) {
|
|
91 logger.severe("Cannot retrieve connections history info: " + e.getMessage());
|
|
92 }
|
|
93 }
|
|
94
|
|
95 private void retrieveUiSettings(Preferences node, ConnectionParams cp) {
|
|
96 byte[] bytes = node.getByteArray(NODE_UI_SETTINGS, new byte[0]);
|
|
97 if (bytes.length != 0) {
|
|
98 try {
|
|
99 UiSettingsData settings = (UiSettingsData) (new ObjectInputStream(
|
|
100 new ByteArrayInputStream(bytes))).readObject();
|
|
101 uiSettingsDataMap.put(cp, settings);
|
|
102 logger.finest("deserialialize: " + settings);
|
|
103 } catch (IOException e) {
|
|
104 logger.info("Cannot deserialize UiSettings: " + e.getMessage());
|
|
105 } catch (ClassNotFoundException e) {
|
|
106 logger.severe("Cannot deserialize UiSettings : " + e.getMessage());
|
|
107 }
|
|
108 }
|
|
109 }
|
|
110
|
|
111 private void retrieveProtocolSettings(Preferences node, ConnectionParams cp) {
|
|
112 byte[] bytes = node.getByteArray(NODE_PROTOCOL_SETTINGS, new byte[0]);
|
|
113 if (bytes.length != 0) {
|
|
114 try {
|
|
115 ProtocolSettings settings = (ProtocolSettings) (new ObjectInputStream(
|
|
116 new ByteArrayInputStream(bytes))).readObject();
|
|
117 settings.refine();
|
|
118 protocolSettingsMap.put(cp, settings);
|
|
119 logger.finest("deserialialize: " + settings);
|
|
120 } catch (IOException e) {
|
|
121 logger.info("Cannot deserialize ProtocolSettings: " + e.getMessage());
|
|
122 } catch (ClassNotFoundException e) {
|
|
123 logger.severe("Cannot deserialize ProtocolSettings : " + e.getMessage());
|
|
124 }
|
|
125 }
|
|
126 }
|
|
127
|
|
128 /**
|
|
129 * Implicit Model interface
|
|
130 */
|
|
131 @SuppressWarnings("UnusedDeclaration")
|
|
132 public LinkedList<ConnectionParams> getConnectionsList() {
|
|
133 return connections;
|
|
134 }
|
|
135
|
|
136 public ProtocolSettings getProtocolSettings(ConnectionParams cp) {
|
|
137 return protocolSettingsMap.get(cp);
|
|
138 }
|
|
139
|
|
140 public UiSettingsData getUiSettingsData(ConnectionParams cp) {
|
|
141 return uiSettingsDataMap.get(cp);
|
|
142 }
|
|
143
|
|
144 public void save() {
|
|
145 try {
|
|
146 cleanStorage();
|
|
147 Preferences connectionsHistoryNode = getConnectionHistoryNode();
|
|
148 int num = 0;
|
|
149 for (ConnectionParams cp : connections) {
|
|
150 if (num >= MAX_ITEMS) break;
|
|
151 if ( ! Strings.isTrimmedEmpty(cp.hostName)) {
|
|
152 addNode(cp, connectionsHistoryNode, num++);
|
|
153 }
|
|
154 }
|
|
155 } catch (AccessControlException ace) { /*nop*/ }
|
|
156 }
|
|
157
|
|
158
|
|
159 public void clear() {
|
|
160 cleanStorage();
|
|
161 init();
|
|
162 }
|
|
163
|
|
164 private void cleanStorage() {
|
|
165 Preferences connectionsHistoryNode = getConnectionHistoryNode();
|
|
166 try {
|
|
167 for (String host : connectionsHistoryNode.childrenNames()) {
|
|
168 connectionsHistoryNode.node(host).removeNode();
|
|
169 }
|
|
170 } catch (BackingStoreException e) {
|
|
171 logger.severe("Cannot remove node: " + e.getMessage());
|
|
172 }
|
|
173 }
|
|
174
|
|
175 private Preferences getConnectionHistoryNode() {
|
|
176 Preferences root = Preferences.userRoot();
|
|
177 return root.node(CONNECTIONS_HISTORY_ROOT_NODE);
|
|
178 }
|
|
179
|
|
180 private void addNode(ConnectionParams connectionParams, Preferences connectionsHistoryNode, int orderNum) {
|
|
181 ProtocolSettings protocolSettings = protocolSettingsMap.get(connectionParams);
|
|
182 UiSettingsData uiSettingsData = uiSettingsDataMap.get(connectionParams);
|
|
183 final Preferences node = connectionsHistoryNode.node(String.valueOf(orderNum));
|
|
184 serializeConnectionParams(node, connectionParams);
|
|
185 serializeProtocolSettings(node, protocolSettings);
|
|
186 serializeUiSettingsData(node, uiSettingsData);
|
|
187 try {
|
|
188 node.flush();
|
|
189 } catch (BackingStoreException e) {
|
|
190 logger.severe("Cannot retrieve connections history info: " + e.getMessage());
|
|
191 }
|
|
192 }
|
|
193
|
|
194 private void serializeUiSettingsData(Preferences node, UiSettingsData uiSettingsData) {
|
|
195 if (uiSettingsData != null) {
|
|
196 try {
|
|
197 ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
|
|
198 ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
|
|
199 objectOutputStream.writeObject(uiSettingsData);
|
|
200 node.putByteArray(NODE_UI_SETTINGS, byteArrayOutputStream.toByteArray());
|
|
201 logger.finest("serialized (" + node.name() + ") " + uiSettingsData);
|
|
202 } catch (IOException e) {
|
|
203 logger.severe("Cannot serialize UiSettings: " + e.getMessage());
|
|
204 }
|
|
205 }
|
|
206 }
|
|
207
|
|
208 private void serializeProtocolSettings(Preferences node, ProtocolSettings protocolSettings) {
|
|
209 if (protocolSettings != null) {
|
|
210 try {
|
|
211 ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
|
|
212 ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
|
|
213 objectOutputStream.writeObject(protocolSettings);
|
|
214 node.putByteArray(NODE_PROTOCOL_SETTINGS, byteArrayOutputStream.toByteArray());
|
|
215 logger.finest("serialized (" + node.name() + ") " + protocolSettings);
|
|
216 } catch (IOException e) {
|
|
217 logger.severe("Cannot serialize ProtocolSettings: " + e.getMessage());
|
|
218 }
|
|
219 }
|
|
220 }
|
|
221
|
|
222 private void serializeConnectionParams(Preferences node, ConnectionParams connectionParams) {
|
|
223 node.put(NODE_HOST_NAME, connectionParams.hostName);
|
|
224 node.putInt(NODE_PORT_NUMBER, connectionParams.getPortNumber());
|
|
225 if (connectionParams.useSsh()) {
|
|
226 node.putBoolean(NODE_USE_SSH, connectionParams.useSsh());
|
|
227 node.put(NODE_SSH_USER_NAME, connectionParams.sshUserName != null ? connectionParams.sshUserName: "");
|
|
228 node.put(NODE_SSH_HOST_NAME, connectionParams.sshHostName != null ? connectionParams.sshHostName: "");
|
|
229 node.putInt(NODE_SSH_PORT_NUMBER, connectionParams.getSshPortNumber());
|
|
230 }
|
|
231 logger.finest("serialized (" + node.name() + ") " + connectionParams.toPrint());
|
|
232 }
|
|
233
|
|
234 public void reorder(ConnectionParams connectionParams) {
|
|
235 reorder(connectionParams, getProtocolSettings(connectionParams), getUiSettingsData(connectionParams));
|
|
236 }
|
|
237
|
|
238 public void reorder(ConnectionParams connectionParams, ProtocolSettings protocolSettings, UiSettings uiSettings) {
|
|
239 reorder(connectionParams, protocolSettings, uiSettings != null? uiSettings.getData() : null);
|
|
240 }
|
|
241
|
|
242 private void reorder(ConnectionParams connectionParams, ProtocolSettings protocolSettings, UiSettingsData uiSettingsData) {
|
|
243 while (connections.remove(connectionParams)) {/*empty - remove all occurrences (if any)*/}
|
|
244 LinkedList<ConnectionParams> cpList = new LinkedList<ConnectionParams>();
|
|
245 cpList.addAll(connections);
|
|
246
|
|
247 connections.clear();
|
|
248 connections.add(new ConnectionParams(connectionParams));
|
|
249 connections.addAll(cpList);
|
|
250 storeSettings(connectionParams, protocolSettings, uiSettingsData);
|
|
251 }
|
|
252
|
|
253 private void storeSettings(ConnectionParams connectionParams, ProtocolSettings protocolSettings, UiSettingsData uiSettingsData) {
|
|
254 if (protocolSettings != null) {
|
|
255 ProtocolSettings savedSettings = protocolSettingsMap.get(connectionParams);
|
|
256 if (savedSettings != null) {
|
|
257 savedSettings.copyDataFrom(protocolSettings);
|
|
258 } else {
|
|
259 protocolSettingsMap.put(new ConnectionParams(connectionParams), new ProtocolSettings(protocolSettings));
|
|
260 }
|
|
261 }
|
|
262 if (uiSettingsData != null) {
|
|
263 uiSettingsDataMap.put(new ConnectionParams(connectionParams), new UiSettingsData(uiSettingsData));
|
|
264 }
|
|
265 }
|
|
266
|
|
267 /**
|
|
268 * Search most suitable connectionParams (cp) from history.
|
|
269 * When history is empty, returns original parameter.
|
|
270 * When original parameter is empty (null or hostName is empty) returns the very first cp from history.
|
|
271 * Then subsequently compare cp from history with original for most fields will match in sequent of
|
|
272 * hostName, portNumber, useSsh, sshHostName, sshPortName, sshPortNumber.
|
|
273 * When any match found it returns.
|
|
274 * When no matches found returns the very first cp from history.
|
|
275 *
|
|
276 * @param orig connectionParams to search
|
|
277 * @return most suitable cp
|
|
278 */
|
|
279 public ConnectionParams getMostSuitableConnection(ConnectionParams orig) {
|
|
280 ConnectionParams res = connections.isEmpty()? orig: connections.get(0);
|
|
281 if (null == orig || Strings.isTrimmedEmpty(orig.hostName)) return res;
|
|
282 for (ConnectionParams cp : connections) {
|
|
283 if (orig.equals(cp)) return cp;
|
|
284 if (compareTextFields(orig.hostName, res.hostName, cp.hostName)) {
|
|
285 res = cp;
|
|
286 continue;
|
|
287 }
|
|
288 if (orig.hostName.equals(cp.hostName) &&
|
|
289 comparePorts(orig.getPortNumber(), res.getPortNumber(), cp.getPortNumber())) {
|
|
290 res = cp;
|
|
291 continue;
|
|
292 }
|
|
293 if (orig.hostName.equals(cp.hostName) &&
|
|
294 orig.getPortNumber() == cp.getPortNumber() &&
|
|
295 orig.useSsh() == cp.useSsh() && orig.useSsh() != res.useSsh()) {
|
|
296 res = cp;
|
|
297 continue;
|
|
298 }
|
|
299 if (orig.hostName.equals(cp.hostName) &&
|
|
300 orig.getPortNumber() == cp.getPortNumber() &&
|
|
301 orig.useSsh() && cp.useSsh() &&
|
|
302 compareTextFields(orig.sshHostName, res.sshHostName, cp.sshHostName)) {
|
|
303 res = cp;
|
|
304 continue;
|
|
305 }
|
|
306 if (orig.hostName.equals(cp.hostName) &&
|
|
307 orig.getPortNumber() == cp.getPortNumber() &&
|
|
308 orig.useSsh() && cp.useSsh() &&
|
|
309 orig.sshHostName != null && orig.sshHostName.equals(cp.hostName) &&
|
|
310 comparePorts(orig.getSshPortNumber(), res.getSshPortNumber(), cp.getSshPortNumber())) {
|
|
311 res = cp;
|
|
312 continue;
|
|
313 }
|
|
314 if (orig.hostName.equals(cp.hostName) &&
|
|
315 orig.getPortNumber() == cp.getPortNumber() &&
|
|
316 orig.useSsh() && cp.useSsh() &&
|
|
317 orig.sshHostName != null && orig.sshHostName.equals(cp.hostName) &&
|
|
318 orig.getSshPortNumber() == cp.getSshPortNumber() &&
|
|
319 compareTextFields(orig.sshUserName, res.sshUserName, cp.sshUserName)) {
|
|
320 res = cp;
|
|
321 }
|
|
322 }
|
|
323 return res;
|
|
324 }
|
|
325
|
|
326 private boolean comparePorts(int orig, int res, int test) {
|
|
327 return orig == test && orig != res;
|
|
328 }
|
|
329
|
|
330 private boolean compareTextFields(String orig, String res, String test) {
|
|
331 return (orig != null && test != null && res != null) &&
|
|
332 orig.equals(test) && ! orig.equals(res);
|
|
333 }
|
|
334
|
|
335 public boolean isEmpty() {
|
|
336 return connections.isEmpty();
|
|
337 }
|
|
338 }
|