View Javadoc

1   /*
2    * Copyright (c) 2005 The University of Reading
3    * All rights reserved.
4    *
5    * Redistribution and use in source and binary forms, with or without
6    * modification, are permitted provided that the following conditions
7    * are met:
8    * 1. Redistributions of source code must retain the above copyright
9    *    notice, this list of conditions and the following disclaimer.
10   * 2. Redistributions in binary form must reproduce the above copyright
11   *    notice, this list of conditions and the following disclaimer in the
12   *    documentation and/or other materials provided with the distribution.
13   * 3. Neither the name of the University of Reading, nor the names of the
14   *    authors or contributors may be used to endorse or promote products
15   *    derived from this software without specific prior written permission.
16   * 
17   * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18   * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20   * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21   * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22   * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23   * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24   * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26   * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27   */
28  
29  package uk.ac.rdg.resc.jstyx.gridservice.client;
30  
31  import javax.swing.JFrame;
32  import javax.swing.JMenuBar;
33  import javax.swing.JMenu;
34  import javax.swing.JMenuItem;
35  import javax.swing.JLabel;
36  import javax.swing.BorderFactory;
37  import javax.swing.KeyStroke;
38  import javax.swing.JOptionPane;
39  import javax.swing.JTree;
40  import javax.swing.JScrollPane;
41  import javax.swing.JSplitPane;
42  
43  import javax.swing.tree.TreeSelectionModel;
44  import javax.swing.tree.TreePath;
45  
46  import java.awt.BorderLayout;
47  
48  import java.awt.event.WindowAdapter;
49  import java.awt.event.WindowEvent;
50  import java.awt.event.KeyEvent;
51  import java.awt.event.ActionListener;
52  import java.awt.event.ActionEvent;
53  import java.awt.event.MouseListener;
54  import java.awt.event.MouseAdapter;
55  import java.awt.event.MouseEvent;
56  
57  import java.net.URL;
58  import java.net.MalformedURLException;
59  
60  import java.util.Vector;
61  
62  import uk.ac.rdg.resc.jstyx.StyxException;
63  import uk.ac.rdg.resc.jstyx.client.StyxConnection;
64  import uk.ac.rdg.resc.jstyx.client.StyxConnectionListener;
65  
66  /***
67   * GUI application for browsing and using Styx Grid Services
68   *
69   * @author Jon Blower
70   * $Revision: 367 $
71   * $Date: 2005-08-30 17:27:20 +0100 (Tue, 30 Aug 2005) $
72   * $Log$
73   * Revision 1.8  2005/08/30 16:27:20  jonblower
74   * Made sure graphical operations operate on the contentPane, not directly on the JFrame
75   *
76   * Revision 1.7  2005/05/26 16:51:40  jonblower
77   * Minor change to dialog box contents
78   *
79   * Revision 1.6  2005/05/18 17:13:51  jonblower
80   * Created SGSInstanceGUI
81   *
82   * Revision 1.5  2005/05/18 08:03:24  jonblower
83   * Implemented creation of new service instances
84   *
85   * Revision 1.4  2005/05/17 18:21:36  jonblower
86   * Added initial pop-up menu support
87   *
88   * Revision 1.3  2005/05/17 15:51:43  jonblower
89   * Correct operation of display of tree of SGS servers, services and instances
90   *
91   * Revision 1.2  2005/05/17 07:52:23  jonblower
92   * Further developments
93   *
94   * Revision 1.1  2005/05/16 16:17:47  jonblower
95   * Initial import
96   *
97   */
98  public class SGSExplorer extends JFrame implements StyxConnectionListener
99  {
100     
101     private JLabel statusBar;
102     private Vector openConnections;
103     private JTree tree;
104     private PropertiesPanel propsPanel; // Properties of the selected node in
105                                         // the tree will appear on this panel
106     private SGSExplorerTreeModel treeModel;
107     
108     /*** Creates a new instance of SGSExplorer */
109     public SGSExplorer()
110     {
111         super("SGSExplorer");
112         this.init();
113     }
114     
115     private void init()
116     {
117         this.getContentPane().setLayout(new BorderLayout());
118         this.setSize(200, 500);
119         this.createMenus();
120         this.createStatusBar();
121         this.setupTree();
122         
123         // Add listener to shut system down cleanly when window is closed
124         this.addWindowListener(new WindowAdapter()
125         {
126             public void windowClosing(WindowEvent we)
127             {
128                 shutdown();
129             }
130         });
131         
132         this.openConnections = new Vector();
133         
134         this.propsPanel = new PropertiesPanel();
135         this.tree.addTreeSelectionListener(this.propsPanel);
136         
137         JSplitPane split = new JSplitPane(JSplitPane.VERTICAL_SPLIT,
138             new JScrollPane(this.tree), new JScrollPane(this.propsPanel));
139         
140         this.getContentPane().add(split, BorderLayout.CENTER);
141         //this.pack();
142     }
143     
144     private void createMenus()
145     {
146         // Create a menu bar
147         JMenuBar menuBar = new JMenuBar();
148         
149         // Create the Service menu (contains items to open connections to 
150         // remote servers, etc)
151         JMenu serviceMenu = new JMenu("Service");
152         serviceMenu.setMnemonic(KeyEvent.VK_S);
153         
154         JMenuItem connectItem = new JMenuItem("Connect to server...");
155         connectItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S,
156             ActionEvent.CTRL_MASK));
157         connectItem.addActionListener(new ActionListener()
158         {
159             public void actionPerformed(ActionEvent e)
160             {
161                 // Open a dialog box for the user to enter the URL to the 
162                 // root of the SGS server
163                 // TODO: allow entry of username etc (certificate?)
164                 String input = JOptionPane.showInputDialog("Enter path to the "
165                     + "server root (e.g. \"myserver:9092\")");
166                 if (input != null)
167                 {
168                     connectToServer(input);
169                 }
170             }
171         });
172         serviceMenu.add(connectItem);
173         
174         serviceMenu.addSeparator();
175         
176         JMenuItem quitItem = new JMenuItem("Quit");
177         quitItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Q,
178             ActionEvent.CTRL_MASK));
179         quitItem.addActionListener(new ActionListener()
180         {
181             public void actionPerformed(ActionEvent e)
182             {
183                 shutdown();
184             }
185         });
186         serviceMenu.add(quitItem);
187         
188         menuBar.add(serviceMenu);
189         
190         this.setJMenuBar(menuBar);
191     }
192     
193     /***
194      * Takes the input from the message dialog box and tries to connect to the
195      * remote server
196      */
197     private void connectToServer(String inputURL)
198     {
199         String host = "";
200         int port = -1;
201         try
202         {
203             // prepend correct protocol if it doesn't already exist
204             if (inputURL.indexOf("://") == -1)
205             {
206                 inputURL = "styx://".concat(inputURL);
207             }
208             URL url = new URL(inputURL);
209             if (!url.getProtocol().equals("styx"))
210             {
211                 JOptionPane.showMessageDialog(null,
212                     "Invalid protocol \"".concat(url.getProtocol()).concat("\""),
213                     "Invalid URL", JOptionPane.ERROR_MESSAGE);
214             }
215             host = url.getHost();
216             port = url.getPort();
217             StyxConnection conn = new StyxConnection(url.getHost(), url.getPort());
218             conn.addListener(this);
219             // Start the connection process. When we have connected successfully
220             // and performed the necessary handshaking, the connectionReady() 
221             // method will be called
222             conn.connectAsync();
223         }
224         catch (MalformedURLException mue)
225         {
226             JOptionPane.showMessageDialog(null,
227                 "Invalid URL: " + mue.getMessage(), "Invalid URL",
228                 JOptionPane.ERROR_MESSAGE);
229         }
230         catch (StyxException se)
231         {
232             // This should not happen unless there's an error with MINA, the 
233             // underlying network architecture
234             JOptionPane.showMessageDialog(null, "Error connecting to " +
235                 host + ":" + port + ": " + se.getMessage(),
236                 "Connection error", JOptionPane.ERROR_MESSAGE);
237         }
238     }
239     
240     private void createStatusBar()
241     {
242         statusBar = new JLabel("Status bar");
243         statusBar.setBorder(BorderFactory.createLoweredBevelBorder());
244         this.getContentPane().add(statusBar, BorderLayout.SOUTH);
245     }
246     
247     /***
248      * Sets up the tree that is used to browse the available SGS servers
249      */
250     private void setupTree()
251     {
252         this.treeModel = new SGSExplorerTreeModel();
253         this.tree = new JTree(this.treeModel);
254         this.tree.addTreeExpansionListener(this.treeModel);
255         this.tree.setRootVisible(false);
256         this.tree.setShowsRootHandles(true);
257         // Ensure that only one node at a time can be selected in the tree
258         this.tree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
259         this.tree.addMouseListener(ml);
260     }
261     
262     /***
263      * Sets the status bar text to the given string
264      */
265     public void setStatusBarText(String text)
266     {
267         this.statusBar.setText(text);
268     }
269     
270     /***
271      * Called just before the application closes. Add code to cleanly shut down
272      * here.
273      */
274     private void shutdown()
275     {
276         // Close all open connections
277         for (int i = 0; i < this.openConnections.size(); i++)
278         {
279             StyxConnection conn = (StyxConnection)this.openConnections.get(i);
280             conn.close();
281         }
282         System.exit(0);
283     }
284     
285     /***
286      * Required by StyxConnectionListener interface. Called when the relevant
287      * handshaking has been performed and the connection is ready for Styx
288      * messages to be sent
289      */
290     public void connectionReady(StyxConnection conn)
291     {
292         System.out.println("Connected to " +
293             conn.getRemoteHost() + ":" + conn.getRemotePort());
294         // TODO: check that this is a SGS server (look for .version file)
295         this.openConnections.add(conn);
296         // Add this connection to the tree model
297         this.treeModel.addNewConnection(conn);
298     }
299     
300     /***
301      * Required by StyxConnectionListener interface.  Called when the connection
302      * has been closed.  Does nothing here.
303      * TODO: use this as signal to remove connection from tree
304      */
305     public void connectionClosed(StyxConnection conn){}
306     
307     /***
308      * Required by StyxConnectionListener interface.  Called when an error has
309      * occurred when connecting.
310      * @param message String describing the problem
311      */
312     public void connectionError(StyxConnection conn, String message)
313     {
314         JOptionPane.showMessageDialog(null, "Could not connect to " +
315             conn.getRemoteHost() + ":" + conn.getRemotePort() + ": "
316             + message, "Connection error", JOptionPane.ERROR_MESSAGE);
317     }
318     
319     /***
320      * Listens for requests for the pop-up menu.
321      * Note that for platform independence we must check for the popup trigger
322      * in both mousePressed and mouseReleased
323      */
324     private MouseListener ml = new MouseAdapter()
325     {
326         public void mousePressed(MouseEvent e)
327         {
328             maybeShowPopup(e);
329         }
330         public void mouseReleased(MouseEvent e)
331         {
332             maybeShowPopup(e);
333         }
334         private void maybeShowPopup(MouseEvent e)
335         {
336             if (e.isPopupTrigger())
337             {
338                 TreePath selPath = tree.getPathForLocation(e.getX(), e.getY());
339                 // If we don't click on something in the tree itself, selPath
340                 // will be null
341                 if (selPath != null)
342                 {
343                     Object lastPathComp = selPath.getLastPathComponent();
344                     if (lastPathComp instanceof CStyxFileNode)
345                     {
346                         // Get the CStyxFileNode that we have clicked on
347                         CStyxFileNode node = (CStyxFileNode)lastPathComp;
348                         StyxExplorerPopupMenu.showContext(node, tree, e.getX(),
349                             e.getY());
350                     }
351                 }
352             }
353         }
354     };
355     
356     public static void main(String[] args)
357     {
358         // Make sure "styx://" URLs are valid
359         System.setProperty("java.protocol.handler.pkgs", "uk.ac.rdg.resc.jstyx.client.protocol");
360         new SGSExplorer().setVisible(true);
361     }
362     
363 }