JAL-3690 refactoring web-services discovery
[jalview.git] / src / jalview / gui / SlivkaPreferences.java
1 package jalview.gui;
2
3 import jalview.bin.Cache;
4 import jalview.util.MessageManager;
5 import jalview.ws.WSDiscovererI;
6 import jalview.ws.slivkaws.SlivkaWSDiscoverer;
7
8 import java.awt.BorderLayout;
9 import java.awt.Color;
10 import java.awt.Component;
11 import java.awt.Dimension;
12 import java.awt.Font;
13 import java.awt.event.ActionEvent;
14 import java.awt.event.ActionListener;
15 import java.awt.event.MouseAdapter;
16 import java.awt.event.MouseEvent;
17 import java.awt.event.MouseListener;
18 import java.net.MalformedURLException;
19 import java.net.URL;
20 import java.util.ArrayList;
21 import java.util.NoSuchElementException;
22
23 import javax.swing.BorderFactory;
24 import javax.swing.Box;
25 import javax.swing.BoxLayout;
26 import javax.swing.JButton;
27 import javax.swing.JOptionPane;
28 import javax.swing.JPanel;
29 import javax.swing.JProgressBar;
30 import javax.swing.JScrollPane;
31 import javax.swing.JTable;
32 import javax.swing.SwingUtilities;
33 import javax.swing.UIManager;
34 import javax.swing.table.AbstractTableModel;
35 import javax.swing.table.DefaultTableCellRenderer;
36
37 @SuppressWarnings("serial")
38 public class SlivkaPreferences extends JPanel
39 {
40   {
41     setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));
42     setPreferredSize(new Dimension(500, 450));
43   }
44
45   WSDiscovererI discoverer;
46
47   private final ArrayList<String> urls = new ArrayList<>();
48
49   private final ArrayList<Integer> statuses = new ArrayList<>();
50
51   private final AbstractTableModel urlTableModel = new AbstractTableModel()
52   {
53     final String[] columnNames = { "Service URL", "Status" };
54
55     @Override
56     public String getColumnName(int col)
57     {
58       return columnNames[col];
59     }
60
61     @Override
62     public Object getValueAt(int rowIndex, int columnIndex)
63     {
64       switch (columnIndex)
65       {
66       case 0:
67         return urls.get(rowIndex);
68       case 1:
69         return statuses.get(rowIndex);
70       default:
71         throw new NoSuchElementException();
72       }
73     }
74
75     @Override
76     public int getRowCount()
77     {
78       return urls.size();
79     }
80
81     @Override
82     public int getColumnCount()
83     {
84       return 2;
85     }
86   };
87
88   private class WSStatusCellRenderer extends DefaultTableCellRenderer
89   {
90     @Override
91     public Component getTableCellRendererComponent(JTable table,
92         Object value, boolean isSelected, boolean hasFocus, int row,
93         int column)
94     {
95       setHorizontalAlignment(CENTER);
96       super.getTableCellRendererComponent(table, "\u25CF", isSelected,
97           hasFocus, row, column);
98       switch ((Integer) value)
99       {
100       case WSDiscovererI.STATUS_NO_SERVICES:
101         setForeground(Color.ORANGE);
102         break;
103       case WSDiscovererI.STATUS_OK:
104         setForeground(Color.GREEN);
105         break;
106       case WSDiscovererI.STATUS_INVALID:
107         setForeground(Color.RED);
108         break;
109       case WSDiscovererI.STATUS_UNKNOWN:
110       default:
111         setForeground(Color.LIGHT_GRAY);
112       }
113       return this;
114     }
115   }
116
117   private JTable urlListTable = new JTable(urlTableModel);
118   {
119     urlListTable.getColumnModel().getColumn(1).setMaxWidth(60);
120     urlListTable.getColumnModel().getColumn(1)
121         .setCellRenderer(new WSStatusCellRenderer());
122   }
123
124   // URL control panel buttons
125   JButton newWsUrl = new JButton(
126       MessageManager.getString("label.new_service_url"));
127
128   JButton editWsUrl = new JButton(
129       MessageManager.getString("label.edit_service_url"));
130
131   JButton deleteWsUrl = new JButton(
132       MessageManager.getString("label.delete_service_url"));
133
134   JButton moveUrlUp = new JButton(
135       MessageManager.getString("action.move_up"));
136
137   JButton moveUrlDown = new JButton(
138       MessageManager.getString("action.move_down"));
139
140   private String showEditUrlDialog(String oldUrl)
141   {
142     String input = (String) JvOptionPane
143         .showInternalInputDialog(
144             this, 
145             MessageManager.getString("label.url:"),
146             UIManager.getString("OptionPane.inputDialogTitle", MessageManager.getLocale()),
147             JOptionPane.QUESTION_MESSAGE,
148             null,
149             null,
150             oldUrl);
151     if (input == null)
152     {
153       return null;
154     }
155     try
156     {
157       new URL(input);
158     } catch (MalformedURLException ex)
159     {
160       JvOptionPane.showInternalMessageDialog(this,
161           MessageManager.getString("label.invalid_url"),
162           UIManager.getString("OptionPane.messageDialogTitle",
163               MessageManager.getLocale()),
164           JOptionPane.WARNING_MESSAGE);
165       return null;
166     }
167     return input;
168   }
169
170   // Button Action Listeners
171   private ActionListener newUrlAction = (ActionEvent e) -> {
172     final String input = showEditUrlDialog("");
173     if (input != null)
174     {
175       urls.add(input);
176       statuses.add(discoverer.getServerStatusFor(input));
177       urlTableModel.fireTableRowsInserted(urls.size(), urls.size());
178       discoverer.setServiceUrls(urls);
179     }
180   };
181
182   private ActionListener editUrlAction = (ActionEvent e) -> {
183     final int i = urlListTable.getSelectedRow();
184     if (i >= 0)
185     {
186       final String input = showEditUrlDialog(urls.get(i));
187       if (input != null)
188       {
189         urls.set(i, input);
190         statuses.set(i, discoverer.getServerStatusFor(input));
191         urlTableModel.fireTableRowsUpdated(i, i);
192         discoverer.setServiceUrls(urls);
193       }
194     }
195   };
196
197   private ActionListener deleteUrlAction = (ActionEvent e) -> {
198     final int i = urlListTable.getSelectedRow();
199     if (i >= 0)
200     {
201       urls.remove(i);
202       statuses.remove(i);
203       urlTableModel.fireTableRowsDeleted(i, i);
204       discoverer.setServiceUrls(urls);
205     }
206   };
207
208   private ActionListener moveUrlUpAction = (ActionEvent e) -> {
209     final int i = urlListTable.getSelectedRow();
210     if (i > 0)
211     {
212       moveTableRow(i, i - 1);
213       discoverer.setServiceUrls(urls);
214     }
215   };
216
217   private ActionListener moveUrlDownAction = (ActionEvent e) -> {
218     final int i = urlListTable.getSelectedRow();
219     if (i >= 0 && i < urls.size() - 1)
220     {
221       moveTableRow(i, i + 1);
222       discoverer.setServiceUrls(urls);
223     }
224   };
225
226   private MouseListener tableClickListener = new MouseAdapter()
227   {
228     final ActionEvent actionEvent = new ActionEvent(urlListTable,
229         ActionEvent.ACTION_PERFORMED, "edit");
230
231     @Override
232     public void mouseClicked(MouseEvent e)
233     {
234       if (e.getClickCount() > 1)
235       {
236         editUrlAction.actionPerformed(actionEvent);
237       }
238     }
239   };
240
241   // Setting up URL list Pane
242   {
243     Font font = new Font("Verdana", Font.PLAIN, 10);
244     JPanel urlPaneContainer = new JPanel(new BorderLayout(5, 5));
245     urlPaneContainer.setBorder(BorderFactory.createCompoundBorder(
246         BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(),
247             "Slivka Web Services"),
248         BorderFactory.createEmptyBorder(10, 5, 5, 5)));
249
250     newWsUrl.setFont(font);
251     editWsUrl.setFont(font);
252     deleteWsUrl.setFont(font);
253     moveUrlUp.setFont(font);
254     moveUrlDown.setFont(font);
255     JPanel editContainer = new JPanel();
256     editContainer.add(newWsUrl);
257     editContainer.add(editWsUrl);
258     editContainer.add(deleteWsUrl);
259     urlPaneContainer.add(editContainer, BorderLayout.PAGE_END);
260
261     JPanel moveContainer = new JPanel();
262     moveContainer
263         .setLayout(new BoxLayout(moveContainer, BoxLayout.PAGE_AXIS));
264     moveContainer.add(moveUrlUp);
265     moveContainer.add(Box.createRigidArea(new Dimension(0, 5)));
266     moveContainer.add(moveUrlDown);
267     urlPaneContainer.add(moveContainer, BorderLayout.LINE_START);
268
269     urlPaneContainer.add(new JScrollPane(urlListTable),
270         BorderLayout.CENTER);
271     this.add(urlPaneContainer);
272
273     // Connecting action listeners
274     urlListTable.addMouseListener(tableClickListener);
275     newWsUrl.addActionListener(newUrlAction);
276     editWsUrl.addActionListener(editUrlAction);
277     deleteWsUrl.addActionListener(deleteUrlAction);
278     moveUrlUp.addActionListener(moveUrlUpAction);
279     moveUrlDown.addActionListener(moveUrlDownAction);
280   }
281
282   private void moveTableRow(int fromIndex, int toIndex)
283   {
284     String url = urls.get(fromIndex);
285     int status = statuses.get(fromIndex);
286     urls.set(fromIndex, urls.get(toIndex));
287     statuses.set(fromIndex, statuses.get(toIndex));
288     urls.set(toIndex, url);
289     statuses.set(toIndex, status);
290     if (urlListTable.getSelectedRow() == fromIndex)
291     {
292       urlListTable.setRowSelectionInterval(toIndex, toIndex);
293     }
294     int firstRow = Math.min(toIndex, fromIndex);
295     int lastRow = Math.max(fromIndex, toIndex);
296     urlTableModel.fireTableRowsUpdated(firstRow, lastRow);
297   }
298
299   // Discoverer reloading buttons
300   JButton refreshServices = new JButton(
301       MessageManager.getString("action.refresh_services"));
302
303   JButton resetServices = new JButton(
304       MessageManager.getString("action.reset_services"));
305
306   JProgressBar progressBar = new JProgressBar();
307
308   // Discoverer buttons action listeners
309   private ActionListener refreshServicesAction = (ActionEvent e) -> {
310     progressBar.setVisible(true);
311     new Thread(() -> {
312       Cache.log.info("Requesting service reload");
313       var task = discoverer.startDiscoverer();
314       try {
315         task.get();
316         Cache.log.info("Reloading done");
317       } catch (Exception exc) {
318         Cache.log.error("Reloading failed", exc);
319       } finally {
320         SwingUtilities.invokeLater(() -> progressBar.setVisible(false));
321       }
322     }).start();
323   };
324
325   private ActionListener resetServicesAction = (ActionEvent e) -> {
326     discoverer.setServiceUrls(null);
327     urls.clear();
328     statuses.clear();
329     urls.addAll(discoverer.getServiceUrls());
330     for (String url : urls)
331     {
332       statuses.add(discoverer.getServerStatusFor(url));
333     }
334     urlTableModel.fireTableDataChanged();
335   };
336
337   {
338     Font font = new Font("Verdana", Font.PLAIN, 11);
339     refreshServices.setFont(font);
340     resetServices.setFont(font);
341     JPanel container = new JPanel();
342     container.add(refreshServices);
343     container.add(resetServices);
344     this.add(container);
345
346     // Connecting action listeners
347     refreshServices.addActionListener(refreshServicesAction);
348     resetServices.addActionListener(resetServicesAction);
349   }
350
351   {
352     progressBar.setVisible(false);
353     progressBar.setIndeterminate(true);
354     add(progressBar);
355   }
356
357   SlivkaPreferences()
358   {
359     // Initial URLs loading
360     discoverer = SlivkaWSDiscoverer.getInstance();
361     urls.addAll(discoverer.getServiceUrls());
362     for (String url : urls)
363     {
364       statuses.add(discoverer.getServerStatusFor(url));
365     }
366   }
367 }