View Javadoc

1   /***
2    * Redistribution and use of this software and associated documentation
3    * ("Software"), with or without modification, are permitted provided
4    * that the following conditions are met:
5    *
6    * 1. Redistributions of source code must retain copyright
7    *    statements and notices.  Redistributions must also contain a
8    *    copy of this document.
9    *
10   * 2. Redistributions in binary form must reproduce the
11   *    above copyright notice, this list of conditions and the
12   *    following disclaimer in the documentation and/or other
13   *    materials provided with the distribution.
14   *
15   * 3. The name "Exolab" must not be used to endorse or promote
16   *    products derived from this Software without prior written
17   *    permission of Exoffice Technologies.  For written permission,
18   *    please contact info@exolab.org.
19   *
20   * 4. Products derived from this Software may not be called "Exolab"
21   *    nor may "Exolab" appear in their names without prior written
22   *    permission of Exoffice Technologies. Exolab is a registered
23   *    trademark of Exoffice Technologies.
24   *
25   * 5. Due credit should be given to the Exolab Project
26   *    (http://www.exolab.org/).
27   *
28   * THIS SOFTWARE IS PROVIDED BY EXOFFICE TECHNOLOGIES AND CONTRIBUTORS
29   * ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
30   * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
31   * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
32   * EXOFFICE TECHNOLOGIES OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
33   * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
34   * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
35   * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
36   * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
37   * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
38   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
39   * OF THE POSSIBILITY OF SUCH DAMAGE.
40   *
41   * Copyright 2000-2003 (C) Exoffice Technologies Inc. All Rights Reserved.
42   *
43   * $Id: AdminMgr.java,v 1.3 2006/02/23 11:02:57 tanderson Exp $
44   *
45   * Date         Author  Changes
46   */
47  
48  
49  package org.exolab.jms.tools.admin;
50  
51  import java.awt.*;
52  import java.awt.event.ActionEvent;
53  import java.awt.event.ActionListener;
54  import java.awt.event.MouseAdapter;
55  import java.awt.event.MouseEvent;
56  import java.awt.event.WindowAdapter;
57  import java.awt.event.WindowEvent;
58  import java.io.BufferedReader;
59  import java.io.IOException;
60  import java.io.InputStream;
61  import java.io.InputStreamReader;
62  import java.io.PrintStream;
63  import java.util.ArrayList;
64  import javax.swing.*;
65  import javax.swing.event.TreeExpansionEvent;
66  import javax.swing.event.TreeExpansionListener;
67  import javax.swing.tree.DefaultMutableTreeNode;
68  import javax.swing.tree.DefaultTreeModel;
69  import javax.swing.tree.TreePath;
70  
71  import org.apache.commons.logging.Log;
72  import org.apache.commons.logging.LogFactory;
73  import org.apache.log4j.xml.DOMConfigurator;
74  import org.apache.oro.text.regex.Pattern;
75  import org.apache.oro.text.regex.PatternMatcherInput;
76  import org.apache.oro.text.regex.Perl5Compiler;
77  import org.apache.oro.text.regex.Perl5Matcher;
78  import org.exolab.jms.config.AdminConfiguration;
79  import org.exolab.jms.config.Configuration;
80  import org.exolab.jms.config.ConfigurationLoader;
81  import org.exolab.jms.util.CommandLine;
82  
83  
84  /***
85   * This class is the Gui controller for the JMS administration. It displays data
86   * as a hierarchical set of tree nodes.
87   * <p/>
88   * <P>The Root is all the contactable JMS servers, idealy there can be several
89   * of these all on different ports, with one common admin port they all listen
90   * to. A user selects a JMSServer then connects via the menu item. This allows
91   * the admin GUI to connect to the server via the main port and begin displaying
92   * all its Queue/Topics and registered consumers.
93   * <p/>
94   * If there are no queue/topics for a consumer the node will be empty. Selecting
95   * a consumer allows a user to see what its details are, i.e last message
96   * received and acked, whether the consumer is currently active/deactive paused
97   * etc.
98   * <p/>
99   * <P>The Gui can also be used to add/remove queue/topics and consumer
100  * registrations.
101  * <p/>
102  * Reliable Topics are read only, since they cannot be persisted, they simply
103  * display a snapshot at the time of connection.
104  * <p/>
105  * <P>For the moment this is not truly dynamic, that is a refresh needs to be
106  * activated on the Gui to cause an update (other than changes made through the
107  * Gui istelf).
108  *
109  * @author <a href="mailto:mourikis@exolab.org">Jim Mourikis</a>
110  * @version $Revision: 1.3 $ $Date: 2006/02/23 11:02:57 $
111  */
112 public class AdminMgr extends JFrame {
113 
114     private JMenu _file;
115     private JMenuItem _exit;
116     private JMenu _connections;
117     private JMenuItem _refresh;
118     private JMenuItem _online;
119     private JMenuItem _offline;
120     private JMenuItem _disconnect;
121     private JMenuItem _startup;
122     private JMenuItem _shutdown;
123     private JTree _serverProperties;
124     private JTextField _messageArea;
125 
126     private Configuration _config;
127 
128     // If this Admin object is connected to any OpenJMS
129     private boolean _connected = false;
130 
131     /***
132      * The server start command
133      */
134     private static String _serverStart = null;
135 
136     /***
137      * The server configuration file path
138      */
139     private static String _serverConfig = null;
140 
141     // redirect stream to local console.
142     private StreamRedirect _output = null;
143 
144     /***
145      * The logger.
146      */
147     private static final Log _log = LogFactory.getLog(AdminMgr.class);
148 
149 
150     public AdminMgr(String path) throws Exception {
151         _config = new ConfigurationLoader().load(path);
152         initComponents(path);
153         pack();
154     }
155 
156     /***
157      * This method is called from within the constructor to initialize the form.
158      * All the GUI objects are created, and callbacks registered.
159      */
160     private void initComponents(String title) {
161         JMenuBar menuBar = new JMenuBar();
162         _file = new JMenu();
163         _exit = new JMenuItem();
164         JMenu actions = new JMenu();
165         _connections = new JMenu();
166         _refresh = new JMenuItem();
167         _online = new JMenuItem();
168         _offline = new JMenuItem();
169         _disconnect = new JMenuItem();
170         JSeparator separator = new JSeparator();
171         _startup = new JMenuItem();
172         _shutdown = new JMenuItem();
173         JScrollPane jmsServers = new JScrollPane();
174         JComboBox jmsCombo = new JComboBox();
175         _serverProperties = new JTree();
176         setTitle("OpenJMS Administrator: " + title);
177         DefaultTreeModel serverModel =
178                 OpenJMSServer.createServerList(_serverProperties);
179         _serverProperties.setModel(serverModel);
180         AdminInfo info = new AdminInfo();
181         _serverProperties.setCellRenderer(info);
182 
183         _messageArea = new JTextField();
184         _file.setText("File");
185         _exit.setToolTipText("Exit administration");
186         _exit.setText("Exit");
187         _exit.setMnemonic('x');
188 
189         _serverProperties.setRootVisible(false);
190         _serverProperties.setShowsRootHandles(true);
191         _serverProperties.putClientProperty("JTree.lineStyle", "Angled");
192         _serverProperties.setCellEditor(new OpenJMSEditor(_serverProperties,
193                 jmsCombo));
194         _serverProperties.setEditable(false);
195         setupCallbacks();
196         _file.add(_exit);
197         _file.setMnemonic('F');
198         menuBar.add(_file);
199         actions.setText("Actions");
200         actions.setMnemonic('A');
201         _connections.setText("Connections");
202         _connections.setMnemonic('C');
203         _refresh.setToolTipText("Refresh the display");
204         _online.setToolTipText("Connect to a running OpenJMS Server");
205         _offline.setToolTipText("Connect directly to an OpenJMS database");
206         _refresh.setText("Refresh");
207         _refresh.setMnemonic('R');
208         actions.add(_refresh);
209 
210         _online.setText("Online");
211         _online.setMnemonic('O');
212         _offline.setText("Offline");
213         _offline.setMnemonic('f');
214         _connections.add(_online);
215         _connections.add(_offline);
216         actions.add(_connections);
217         _disconnect.setToolTipText
218                 ("Disconnect from any connected OpenJMS Servers");
219         _disconnect.setText("Disconnect");
220         _disconnect.setMnemonic('D');
221         actions.add(_disconnect);
222 
223         actions.add(separator);
224         _startup.setToolTipText("Start the OpenJMS server");
225         _startup.setText("Start OpenJMS");
226         _startup.setMnemonic('S');
227         _shutdown.setToolTipText("Shutdown the connected OpenJMS server");
228         _shutdown.setText("Shutdown OpenJMS");
229         _shutdown.setMnemonic('h');
230         actions.add(_startup);
231         actions.add(_shutdown);
232         menuBar.add(actions);
233 
234         jmsServers.setViewportView(_serverProperties);
235 
236 
237         getContentPane().add(jmsServers, BorderLayout.CENTER);
238 
239         _messageArea.setToolTipText("Message Area");
240         _messageArea.setEditable(false);
241         _messageArea.setForeground(java.awt.Color.red);
242         _messageArea.setText("Not Connected");
243         _messageArea.setHorizontalAlignment(SwingConstants.CENTER);
244 
245 
246         getContentPane().add(_messageArea, BorderLayout.SOUTH);
247         setJMenuBar(menuBar);
248         _startup.setEnabled(true);
249         _shutdown.setEnabled(false);
250         _refresh.setEnabled(false);
251         _disconnect.setEnabled(false);
252     }
253 
254     /***
255      * The exit method for the application, when the user shutdowns the form.
256      */
257     private void exitAdmin() {
258         System.exit(0);
259     }
260 
261     /***
262      * Exit the Application when a user selects File->Exit from the menu
263      */
264     private void exitForm() {
265         System.exit(0);
266     }
267 
268     /***
269      * Refresh the display, and repaint all items.
270      */
271     private void refresh() {
272         if (AbstractAdminConnection.instance() instanceof OnlineConnection) {
273             setConnected(false, null);
274             setConnected(true, "Connected - Online Mode");
275             _startup.setEnabled(false);
276             _shutdown.setEnabled(true);
277 
278         } else {
279             ((OpenJMSServer) (_serverProperties.getModel().getRoot()
280             )).refresh();
281         }
282     }
283 
284     /***
285      * Start the OpenJMSServer.
286      */
287     private void startup() {
288 
289         try {
290             String args[] = getStartCommand();
291 
292             System.out.print("running ");
293             for (int i = 0, j = args.length; i < j; i++) {
294                 System.out.print(args[i] + " ");
295             }
296             System.out.println();
297 
298             if (_output != null) {
299                 // Stop the old redirect if any.
300                 _output.interrupt();
301             }
302             Process proc = Runtime.getRuntime().exec(args);
303             // Redirect output
304             _output = new StreamRedirect(proc.getInputStream());
305             // kick it off
306             _output.start();
307         } catch (Exception err) {
308             JOptionPane.showMessageDialog
309                     (this, "Failed to Startup OpenJMSServer:\n" + err
310                             + "\nCheck config file",
311                             "OpenJMSServer Startup", JOptionPane.ERROR_MESSAGE);
312         }
313     }
314 
315     /***
316      * When a user wishes to connect to all known OpenJMSServers. Attempt to
317      * create an RMI connection to the OpenJMSServer. If the server is not
318      * running, this will fail. The user can start the server through the start
319      * server command, and attempt to re-connect, or use the offline method
320      * below.
321      */
322     private void onlineConnect() {
323         try {
324             // if online.
325             new OnlineConnection(this, _config);
326             _startup.setEnabled(false);
327             _shutdown.setEnabled(true);
328             setConnected(true, "Connected - Online Mode");
329         } catch (Exception err) {
330             JOptionPane.showMessageDialog
331                     (this, err.getMessage(), "Online Connection Error",
332                             JOptionPane.ERROR_MESSAGE);
333         }
334     }
335 
336 
337     /***
338      * Connect to the database in offline mode.
339      */
340     private void offlineConnect() {
341         try {
342             // if online.
343             new OfflineConnection(this, _config);
344             _startup.setEnabled(false);
345             _shutdown.setEnabled(false);
346             setConnected(true, "Connected - OFFLine Mode");
347         } catch (Exception err) {
348             error("Database error", err.getMessage(), err);
349         }
350     }
351 
352     /***
353      * Disconnect from a connected OpenJMSServer. Close the database, set the
354      * connected flag to false, stop displaying the OpenJMS folder.
355      */
356     private void disconnect() {
357         try {
358             AbstractAdminConnection.instance().close();
359             setConnected(false, null);
360         } catch (Exception e) {
361             JOptionPane.showMessageDialog
362                     (this, e.getMessage(), "Database Close Error",
363                             JOptionPane.ERROR_MESSAGE);
364         }
365     }
366 
367     /***
368      * A conveniance routine to open/close all database connections, and fix up
369      * the display.
370      * <p/>
371      * <P>When connecting, show the root object, get any persistent queue/topic
372      * names currently in the db, and display them. Turn off the connection
373      * menu, enable the disconnection and shutdown and the context menus. Set
374      * the message area text to connected.
375      * <p/>
376      * <P>When disconnecting, turn off the root, destroy all Gui objects close
377      * the db connection, turn off all context sensitive menus, disable
378      * disconnection menu and enable connection. Set the message text to
379      * disconnected.
380      *
381      * @param c a flag inidication if this is a connection or disconnection.
382      */
383     private void setConnected(boolean c, String st) {
384         if (c) {
385             _serverProperties.setRootVisible(true);
386             ((OpenJMSServer)
387                     (_serverProperties.getModel().getRoot())).displayConnections();
388             _connections.setEnabled(false);
389             _refresh.setEnabled(true);
390             // _shutdown.setEnabled(true);
391             _disconnect.setEnabled(true);
392             // _startup.setEnabled(false);
393             _messageArea.setForeground(java.awt.Color.green.darker().darker());
394             _messageArea.setText(st);
395             _connected = true;
396         } else {
397             _serverProperties.setRootVisible(false);
398             OpenJMSServer root =
399                     (OpenJMSServer) _serverProperties.getModel().getRoot();
400             root.removeAllChildren();
401             DefaultTreeModel model =
402                     (DefaultTreeModel) _serverProperties.getModel();
403             model.nodeStructureChanged((DefaultMutableTreeNode) root);
404             _connections.setEnabled(true);
405             _startup.setEnabled(true);
406             _shutdown.setEnabled(false);
407             _refresh.setEnabled(false);
408             _disconnect.setEnabled(false);
409             _messageArea.setForeground(java.awt.Color.red);
410             _messageArea.setText("Not Connected");
411             _connected = false;
412         }
413     }
414 
415     /***
416      * Set up all Action menu callbacks, and mouse events for the tree and its
417      * nodes. Check all mose 2 key presses on a node, select the node, then call
418      * the nodes appropriate display methos to display its specific popup
419      * menus.
420      * <p/>
421      * <P>When first connected, all queue/topics displayed are not expaned. This
422      * is just a performance saver, since their could potentially be hundreds of
423      * objects. A callback is set up, so that when a queue/topic is expanded, it
424      * is lokked up only then to determine what consumers are registered with
425      * it.
426      */
427     private void setupCallbacks() {
428 
429         addWindowListener(new WindowAdapter() {
430 
431             public void windowClosing(WindowEvent evt) {
432                 exitForm();
433             }
434         }
435         );
436 
437 
438         _serverProperties.addMouseListener(new MouseAdapter() {
439 
440             public void mousePressed(MouseEvent e) {
441                 if (!_connected) {
442                     return;
443                 }
444 
445                 if (SwingUtilities.isRightMouseButton(e)) {
446                     int selRow = _serverProperties.getRowForLocation
447                             (e.getX(), e.getY());
448 
449                     _serverProperties.setSelectionRow(selRow);
450                     Object loc =
451                             _serverProperties.getLastSelectedPathComponent();
452                     if (loc instanceof OpenJMSNode) {
453                         OpenJMSNode node = (OpenJMSNode) loc;
454                         node.displayCommands
455                                 (_serverProperties.getRowBounds(selRow));
456                     } else if (loc instanceof OpenJMSServer) {
457                         ((OpenJMSServer) loc).displayCommands
458                                 (_serverProperties.getRowBounds(selRow));
459                     }
460                 }
461             }
462         }
463         );
464 
465         _serverProperties.addTreeExpansionListener(new TreeExpansionListener() {
466 
467             public void treeCollapsed(TreeExpansionEvent e) {
468                 // todo Anything.....
469             }
470 
471             public void treeExpanded(TreeExpansionEvent e) {
472                 TreePath path = e.getPath();
473                 Object loc = path.getLastPathComponent();
474                 if (loc instanceof OpenJMSNode) {
475                     OpenJMSNode node = (OpenJMSNode) loc;
476                     node.update();
477                 }
478             }
479         }
480         );
481 
482         /***
483          _serverProperties.addTreeSelectionListener(new TreeSelectionListener()
484          {
485          public void valueChanged(TreeSelectionEvent e)
486          {
487          TreePath path = e.getPath();
488          Object loc = path.getLastPathComponent();
489          if (loc instanceof OpenJMSNode)
490          {
491          OpenJMSNode node = (OpenJMSNode)loc;
492          System.out.println(node);
493          }
494          }
495          }
496          );
497 
498          **/
499         _exit.addActionListener(new ActionListener() {
500 
501             public void actionPerformed(ActionEvent evt) {
502                 exitAdmin();
503             }
504         }
505         );
506 
507 
508         _refresh.addActionListener(new ActionListener() {
509 
510             public void actionPerformed(ActionEvent evt) {
511                 refresh();
512             }
513         }
514         );
515 
516 
517         _online.addActionListener(new ActionListener() {
518 
519             public void actionPerformed(ActionEvent evt) {
520                 onlineConnect();
521             }
522         }
523         );
524 
525         _offline.addActionListener(new ActionListener() {
526             public void actionPerformed(ActionEvent evt) {
527                 offlineConnect();
528             }
529         }
530         );
531 
532         _disconnect.addActionListener(new ActionListener() {
533             public void actionPerformed(ActionEvent evt) {
534                 disconnect();
535             }
536         }
537         );
538 
539         _startup.addActionListener(new ActionListener() {
540             public void actionPerformed(ActionEvent evt) {
541                 startup();
542             }
543         }
544         );
545 
546         _shutdown.addActionListener(new ActionListener() {
547 
548             public void actionPerformed(ActionEvent evt) {
549                 try {
550                     AbstractAdminConnection.instance().stopServer();
551                     setConnected(false, null);
552                 } catch (NullPointerException err) {
553                     JOptionPane.showMessageDialog
554                             (_file, "Must connect with online mode \nto "
555                                     + "shutdown server", "Shutdown Error",
556                                     JOptionPane.ERROR_MESSAGE);
557                 }
558             }
559         }
560         );
561     }
562 
563     /***
564      * The main entry point for this admin gui. The main form and any support
565      * dialogs are created. An initial size is given, and the gui placed in the
566      * middle of the screen
567      *
568      * @param args the command line arguments
569      */
570     public static void main(String args[]) {
571         try {
572             CommandLine cmdline = new CommandLine(args);
573 
574             boolean helpSet = cmdline.exists("help");
575             boolean configSet = cmdline.exists("config");
576             boolean stopServer = cmdline.exists("stopServer");
577             String username = cmdline.value("u");
578             String password = cmdline.value("p");
579 
580             if (helpSet) {
581                 usage();
582             } else if (!configSet && !stopServer && args.length != 0) {
583                 // invalid argument specified
584                 usage();
585             } else {
586                 String configFile = cmdline.value("config");
587                 if (configFile == null) {
588                     String home = getOpenJMSHome();
589                     configFile = home + "/config/openjms.xml";
590                 }
591                 Configuration config = new ConfigurationLoader().load(configFile);
592                 String path = config.getLoggerConfiguration().getFile();
593                 if (path != null) {
594                     DOMConfigurator.configure(path);
595                 }
596                 AdminConfiguration adminConfig = null;
597 
598                 adminConfig = config.getAdminConfiguration();
599                 _serverStart = adminConfig.getScript();
600                 _serverConfig = adminConfig.getConfig();
601                 if (_serverConfig == null) {
602                     _serverConfig = configFile;
603                 }
604 
605                 if (stopServer) {
606                     // this is a special mode that will just attempt
607                     // a connection to the server and stop it. No GUI
608 
609                     new OnlineConnection(username, password, config);
610                     AbstractAdminConnection.instance().stopServer();
611                 } else {
612                     AdminMgr admin = new AdminMgr(configFile);
613                     QueryDialog.create(admin);
614                     CreateQueueDialog.create(admin);
615                     CreateTopicDialog.create(admin);
616                     CreateLogonDialog.create(admin);
617                     CreateUserDialog.create(admin);
618                     ChangePasswordDialog.create(admin);
619                     Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
620                     // About center of screen
621                     admin.setLocation(screen.width / 2 - 150, screen.height / 2 - 150);
622                     admin.setSize(300, 300);
623                     admin.invalidate();
624                     admin.show();
625                 }
626             }
627         } catch (Exception err) {
628             err.printStackTrace();
629             System.err.println("Failed to initialize AdminMgr.\nExiting....");
630         }
631     }
632 
633     /***
634      * Print out information on running this sevice
635      */
636     static protected void usage() {
637         PrintStream out = System.out;
638 
639         out.println("\n\n");
640         out.println("=====================================================");
641         out.println("Usage information for " + AdminMgr.class.getName());
642         out.println("=====================================================");
643         out.println("\n" + AdminMgr.class.getName());
644         out.println("    [-help | -config <xml config file>]\n");
645         out.println("\t-help   displays this screen\n");
646         out.println("\t-config file name of xml-based config file\n");
647     }
648 
649     /***
650      * A simple class to re-direct the output stream from JMS to the local
651      * console
652      */
653     class StreamRedirect extends Thread {
654 
655         InputStream is_;
656 
657         StreamRedirect(InputStream is) {
658             is_ = is;
659         }
660 
661         public void run() {
662             try {
663                 InputStreamReader isr = new InputStreamReader(is_);
664                 BufferedReader br = new BufferedReader(isr);
665                 String line = null;
666                 while ((line = br.readLine()) != null) {
667                     System.out.println(line);
668                 }
669             } catch (IOException ioe) {
670                 ioe.printStackTrace();
671             }
672         }
673     }
674 
675     private String[] getStartCommand() throws Exception {
676         ArrayList args = new ArrayList();
677 
678         if (_serverStart != null) {
679             Perl5Compiler compiler = new Perl5Compiler();
680             Pattern pattern = compiler.compile("'.*'|[^//s]*");
681             Perl5Matcher matcher = new Perl5Matcher();
682             PatternMatcherInput input = new PatternMatcherInput(_serverStart);
683 
684             while (matcher.contains(input, pattern)) {
685                 String arg = matcher.getMatch().toString();
686                 if (arg.startsWith("'") && arg.endsWith("'")) {
687                     arg = arg.substring(1, arg.length() - 1);
688                 }
689                 args.add(arg);
690             }
691         }
692 
693         args.add("-config");
694         args.add(_serverConfig);
695 
696         return (String[]) args.toArray(new String[0]);
697     }
698 
699     /***
700      * Returns the value of the openjms.home environment variable
701      */
702     private static String getOpenJMSHome() {
703         return System.getProperty("openjms.home",
704                 System.getProperty("user.dir"));
705     }
706 
707     /***
708      * Display and log an error
709      *
710      * @param title
711      * @param message
712      * @param exception
713      */
714     private void error(String title, String message, Exception exception) {
715         _log.error(message, exception);
716         JOptionPane.showMessageDialog(this, message, title,
717                 JOptionPane.ERROR_MESSAGE);
718     }
719 
720 } //-- AdminMgr
721