1 /*------------------------------------------------------------------------------
  2 Name:      MainGUI.java
  3 Project:   xmlBlaster.org
  4 Copyright: xmlBlaster.org, see xmlBlaster-LICENSE file
  5 Comment:   Main class to invoke the xmlBlaster server
  6 Version:   $Id: MainGUI.java 14922 2006-03-12 23:12:20Z ruff $
  7 ------------------------------------------------------------------------------*/
  8 package org.xmlBlaster;
  9 
 10 import java.util.logging.LogRecord;
 11 import java.util.logging.Logger;
 12 import java.util.logging.Level;
 13 import org.xmlBlaster.util.StopWatch;
 14 
 15 import org.xmlBlaster.util.XmlBlasterException;
 16 import org.xmlBlaster.util.MsgUnitRaw;
 17 import org.xmlBlaster.util.def.Constants;
 18 import org.xmlBlaster.util.log.XbNotifyHandler;
 19 import org.xmlBlaster.engine.ServerScope;
 20 import org.xmlBlaster.engine.qos.AddressServer;
 21 import org.xmlBlaster.protocol.I_Authenticate;
 22 import org.xmlBlaster.protocol.I_XmlBlaster;
 23 import org.xmlBlaster.client.qos.ConnectQos;
 24 import org.xmlBlaster.client.qos.ConnectReturnQos;
 25 import org.xmlBlaster.client.key.GetKey;
 26 
 27 import java.awt.BorderLayout;
 28 import java.awt.Button;
 29 import java.awt.Checkbox;
 30 import java.awt.Color;
 31 import java.awt.Font;
 32 import java.awt.Frame;
 33 import java.awt.GridBagConstraints;
 34 import java.awt.GridBagLayout;
 35 import java.awt.GridLayout;
 36 import java.awt.Image;
 37 import java.awt.Insets;
 38 import java.awt.Label;
 39 import java.awt.Panel;
 40 import java.awt.TextArea;
 41 import java.awt.TextField;
 42 import java.awt.Toolkit;
 43 import java.awt.event.*;
 44 import java.util.Vector;
 45 
 46 import org.jacorb.poa.gui.beans.FillLevelBar;
 47 
 48 
 49 /**
 50  * Start xmlBlaster with a GUI based control panel.
 51  * <p />
 52  * A control panel pops up, where you can<br />
 53  * <ul>
 54  *   <li>Stop xmlBlaster</li>
 55  *   <li>View the performance monitor</li>
 56  *   <li>See and adjust logging output</li>
 57  *   <li>Invoke XPath queries on messages in xmlBlaster</li>
 58  * </ul>
 59  * The available start parameters are similar to Main
 60  * <p>
 61  * The login name "__sys__GuiQuery" is reserved!<br />
 62  * </p>
 63  * @see org.xmlBlaster.Main
 64  */
 65 public class MainGUI extends Frame implements Runnable, org.xmlBlaster.util.log.I_LogListener
 66 {
 67    private static final long serialVersionUID = 1L;
 68    private ServerScope glob;
 69    private static Logger log = Logger.getLogger(MainGUI.class.getName());
 70 
 71    private Toolkit toolkit = Toolkit.getDefaultToolkit();
 72    /** The xmlBlaster server, is set from Main() constructor */
 73    org.xmlBlaster.Main xmlBlasterMain = null;
 74 
 75    private Button exitButton;
 76    private Button hideButton;
 77    private Button clearLogButton;
 78    private Button dumpButton;
 79 
 80    /** TextArea with scroll bars for logging output. */
 81    private TextArea logOutput = null;
 82    /** To save memory consumption, limit number of logging lines to this value. */
 83    private final long MAX_LOG_LINES = 3000;
 84    /** The actual number of logged lines in the TextArea. */
 85    private long numLogLines = 0;
 86 
 87    /** Approximate elapsed time since startup of this server. */
 88    private long elapsedTime = 0L;
 89    /** Time when xmlBlaster was started */
 90    private long startupTime = 0L;
 91    /** Last time the performance was evaluated */
 92    private long lastPollingTime = 0L;
 93 
 94    /** Performance monitor for number of published messages. */
 95    private FillLevelBar publishedMessagesBar = new FillLevelBar();
 96    private Label publishedLabel = new Label(); // display total count
 97    private int peakPublishedMessages = 0;
 98    private long publishedMessages = 0L;
 99    private long lastPublishedMessages = 0L;
100 
101    /** Performance monitor for number of update messages (callbacks to clients). */
102    private FillLevelBar sentMessagesBar = new FillLevelBar();
103    private Label sentLabel = new Label();
104    private int peakSentMessages = 0;
105    private long sentMessages = 0L;
106    private long lastSentMessages = 0L;
107 
108    /** Performance monitor for number of synchronous accessed messages. */
109    private FillLevelBar getMessagesBar = new FillLevelBar();
110    private Label getLabel = new Label();
111    private int peakGetMessages = 0;
112    private long getMessages = 0L;
113    private long lastGetMessages = 0L;
114 
115    /** XPath query input field. */
116    private TextField  inputTextField = new TextField();
117    /** Display XPath query results. */
118    private TextArea queryOutput = null;
119    /** A client accessing xmlBlaster to do some XPath query. */
120    private GuiQuery clientQuery = null;
121    /** Remember previous query strings. */
122    private QueryHistory queryHistory;
123 
124 
125    /**
126     * Construct the xmlBlaster GUI.
127     */
128    public MainGUI(ServerScope glob, org.xmlBlaster.Main main)
129    {
130       this.xmlBlasterMain = main;
131       this.glob = glob;
132 
133 
134       // set the application icon
135       java.net.URL oUrl;
136       oUrl = this.getClass().getResource("AppIcon.gif");
137       Image img = null;
138       if (oUrl != null)
139          img = java.awt.Toolkit.getDefaultToolkit().getImage(oUrl);
140       if(img != null)
141       {
142         this.setIconImage(img);
143         System.out.println(img.toString());
144       }
145       else
146       {
147         System.out.println("AppIcon.gif not found");
148       }
149 
150 
151       setTitle("XmlBlaster Control Panel");
152       init();
153 
154       // Poll xmlBlaster internal states
155       PollingThread poller = new PollingThread(this);
156       poller.start();
157 
158    }
159 
160 
161    /**
162     * Start the GUI thread.
163     */
164    public void run()
165    {
166       show();
167       if (this.xmlBlasterMain == null)
168          this.xmlBlasterMain = new org.xmlBlaster.Main(glob, this);
169    }
170 
171    /**
172     * Event fired by Logger.java through interface I_LogListener. 
173     * <p />
174     * log.addLogDevice(this);
175     * <p />
176     * Log output into TextArea<br />
177     * If the number of lines displayed is too big, cut half of them
178     */
179    public void log(LogRecord record)
180    {
181       String str = record.getLevel().toString() + " [" + record.getLoggerName() + "] " + record.getMessage();
182       if (logOutput == null) {
183          System.err.println(str + "\n");
184          return;
185       }
186       if (numLogLines > MAX_LOG_LINES) {
187          String text = logOutput.getText();
188          text = text.substring(text.length()/2, text.length());
189          logOutput.setText(text);
190       }
191       numLogLines++;
192       logOutput.append(str + "\n");
193    }
194 
195    /**
196     * Event fired every 1 seconds by the PollingThread.
197     * <p />
198     * Update the statistic bars.
199     * @param sleepTime Milliseconds how long the PollingThread was sleeping (no zero division check!)
200     */
201    void pollEvent(long sleepTime)
202    {
203       long current = System.currentTimeMillis();
204       if (lastPollingTime > 0L) {
205          sleepTime = current - lastPollingTime; // correct sleepTime with the real sleeping time
206       }
207       lastPollingTime = current;
208       elapsedTime += current - startupTime;
209 
210       double sleepSeconds = sleepTime / 1000.0;
211       //double elapsedSeconds = elapsedTime / 1000.0;
212 
213       {
214          publishedMessages = this.glob.getRequestBroker().getNumPublish();
215          int currentPublishedAvg = (int)((publishedMessages - lastPublishedMessages)/sleepSeconds);
216          if ((publishedMessages - lastPublishedMessages) == 1) currentPublishedAvg = 1;
217          //int totalPublishedAvg = (int)(numPublish/elapsedSeconds);
218          publishedMessagesBar.setCurrentValue(currentPublishedAvg);
219          if (currentPublishedAvg > peakPublishedMessages) {
220             peakPublishedMessages = currentPublishedAvg;
221             publishedMessagesBar.setAvgValue(peakPublishedMessages);
222          }
223          //publishedMessagesBar.setAvgValue(totalPublishedAvg);
224          publishedLabel.setText("Total:  " + publishedMessages);
225          lastPublishedMessages = publishedMessages;
226       }
227 
228       {
229          sentMessages = 0;// TODO SessionInfo.sentMessages;
230          int currentSentAvg = (int)((sentMessages - lastSentMessages)/sleepSeconds);
231          if ((sentMessages - lastSentMessages) == 1) currentSentAvg = 1;
232          //int totalSentAvg = (int)(sentMessages/elapsedSeconds);
233          sentMessagesBar.setCurrentValue(currentSentAvg);
234          if (currentSentAvg > peakSentMessages) {
235             peakSentMessages = currentSentAvg;
236             sentMessagesBar.setAvgValue(peakSentMessages);
237          }
238          // sentMessagesBar.setAvgValue(totalSentAvg);
239          sentLabel.setText("Total:  " + sentMessages);
240          lastSentMessages = sentMessages;
241       }
242 
243       {
244          getMessages = this.glob.getRequestBroker().getNumGet();
245          int currentGetAvg = (int)((getMessages - lastGetMessages)/sleepSeconds);
246          if ((getMessages - lastGetMessages) == 1) currentGetAvg = 1;
247          //int totalGetAvg = (int)(numGet/elapsedSeconds);
248          // System.out.println("totally numGet=" + numGet + " current avg=" + currentGetAvg + " total avg=" + totalGetAvg);
249          getMessagesBar.setCurrentValue(currentGetAvg);
250          if (currentGetAvg > peakGetMessages) {
251             peakGetMessages = currentGetAvg;
252             getMessagesBar.setAvgValue(peakGetMessages);
253          }
254          // getMessagesBar.setAvgValue(totalGetAvg);
255          getLabel.setText("Total:  " + getMessages);
256          lastGetMessages = getMessages;
257       }
258    }
259 
260    private void registerLogEvents() {
261       XbNotifyHandler.instance().register(Level.ALL.intValue(), this);
262    }
263 
264    private void unregisterLogEvents() {
265       XbNotifyHandler.instance().unregister(Level.ALL.intValue(), this);
266    }
267    
268    /**
269     * Build the GUI layout.
270     */
271    private void init()
272    {
273       registerLogEvents();
274       setLayout(new GridBagLayout());
275       GridBagConstraints gbc = new GridBagConstraints();
276       gbc.fill = GridBagConstraints.BOTH;
277       gbc.insets = new Insets(5,5,5,5);
278 
279       // Exit Button
280       exitButton = new Button("Exit");
281       class BeepListener implements ActionListener {
282          public void actionPerformed(ActionEvent e) {
283             toolkit.beep();
284             if (clientQuery != null)
285                clientQuery.logout();
286             //unregisterLogEvents();
287             log.info("Good bye!");
288             System.exit(0);
289          }
290       }
291       exitButton.addActionListener(new BeepListener());
292       gbc.gridx=0; gbc.gridy=0; gbc.gridwidth=1; gbc.gridheight=1;
293       gbc.weightx = gbc.weighty = 0.0;
294       add(exitButton, gbc);
295 
296       // Hide Button
297       hideButton = new Button("Hide Window");
298       class HideListener implements ActionListener {
299          public void actionPerformed(ActionEvent e) {
300             hideWindow();
301          }
302       }
303       hideButton.addActionListener(new HideListener());
304       gbc.gridx=1; gbc.gridy=0; gbc.gridwidth=1; gbc.gridheight=1;
305       gbc.weightx = gbc.weighty = 0.0;
306       add(hideButton, gbc);
307 
308       // Statistic display with fill level bars
309       int offset = 0;
310       gbc.gridx=offset; gbc.gridy=1; gbc.gridwidth=1; gbc.gridheight=1;
311       gbc.weightx = gbc.weighty = 0.0;
312       createBarPanel(publishedMessagesBar, publishedLabel, "Published", gbc, offset++);
313       createBarPanel(sentMessagesBar,      sentLabel,      "Update",    gbc, offset++);
314       createBarPanel(getMessagesBar,       getLabel,       "Get",       gbc, offset++);
315 
316 
317       {  // XPath query GUI
318          Panel panel = new Panel();
319          panel.setName("QueryPanel");
320          panel.setLayout(new BorderLayout());
321          panel.setBackground(java.awt.SystemColor.control);
322 
323          {  // Field to enter XPath text
324             Panel inputPanel = new Panel();
325             inputPanel.setLayout(new BorderLayout());
326 
327             Label inputLabel = new Label("XPath query: ");
328             inputPanel.add("West", inputLabel);
329 
330             inputTextField.setText("//key");
331             inputPanel.add("Center", inputTextField);
332             inputTextField.addKeyListener(new XPathKeyListener());
333 
334             panel.add("North", inputPanel);
335          }
336 
337          {  // TextArea to show query results
338             queryOutput = new TextArea();
339             queryOutput.setEditable(false);
340 
341             panel.add("South", queryOutput);
342          }
343 
344          gbc.gridx=offset; gbc.gridy=1; gbc.gridwidth=3; gbc.gridheight=1;
345          add(panel, gbc);
346       }
347 
348 
349       // Checkboxes for log levels
350       gbc.gridx=0; gbc.gridy=2; gbc.gridwidth=1; gbc.gridheight=1;
351       add(new Label("Choose Logging Level: "), gbc);
352       gbc.gridx=1; gbc.gridwidth=3; gbc.gridheight=1;
353       add(createLogLevelBoxes(), gbc);
354 
355 
356       // Clear logging output - Button
357       clearLogButton = new Button("Clear Log Window");
358       class ClearListener implements ActionListener {
359          public void actionPerformed(ActionEvent e) {
360             logOutput.setText("");
361          }
362       }
363       clearLogButton.addActionListener(new ClearListener());
364       gbc.gridx=4; gbc.gridy=2; gbc.gridwidth=1; gbc.gridheight=1;
365       gbc.weightx = gbc.weighty = 0.0;
366       add(clearLogButton, gbc);
367 
368 
369       // Dump internal state - Button
370       dumpButton = new Button("Dump State");
371       class DumpListener implements ActionListener {
372          public void actionPerformed(ActionEvent e) {
373             // logOutput.setText("");  // clear log window
374             try {
375                log.info("Dump start");
376                I_Authenticate auth = xmlBlasterMain.getAuthenticate();
377                StringBuffer buf = new StringBuffer(auth.toXml());
378                buf.append(xmlBlasterMain.getXmlBlaster().toXml());
379                LogRecord record = new LogRecord(Level.INFO, buf.toString());
380                log(record);
381                log.info("Dump end");
382             }
383             catch(XmlBlasterException ee) {
384                log.severe("Sorry, dump failed: " + ee.getMessage());
385             }
386          }
387       }
388       dumpButton.addActionListener(new DumpListener());
389       gbc.gridx=5; gbc.gridy=2; gbc.gridwidth=1; gbc.gridheight=1;
390       gbc.weightx = gbc.weighty = 0.0;
391       add(dumpButton, gbc);
392 
393 
394       // TextArea for log outputs
395       gbc.gridx=0; gbc.gridy=3; gbc.gridwidth=6; gbc.gridheight=6;
396       gbc.weightx = gbc.weighty = 1.0;
397       logOutput = new TextArea("", 30, 100); // set rows here (width 100 is ignored)
398       logOutput.setEditable(false);
399       add(logOutput, gbc);
400 
401       pack();
402 
403       startupTime = System.currentTimeMillis();
404    }
405 
406 
407    /**
408     * Hide the window.
409     * Note that all the resources are still busy, only logging is directed to console
410     */
411    private void hideWindow()
412    {
413       unregisterLogEvents();
414       if (isShowing()) {
415          log.info("Press <g> and <Enter> to popup the GUI again (press ? for other options).");
416          setVisible(false); // dispose(); would clean up all resources
417       }
418    }
419 
420 
421    /**
422     * Hide the window.
423     * Note that all the resources are still busy, only logging is directed to console
424     */
425    void showWindow()
426    {
427       if (!isShowing()) {
428          registerLogEvents();
429          if (log.isLoggable(Level.FINE)) log.fine("Show window again ...");
430          show();
431       }
432    }
433 
434 
435    /**
436     * Get the events when a Checkbox is selected with the mouse.
437     */
438    class LogLevelListener implements ItemListener
439    {
440       public void itemStateChanged(ItemEvent e)
441       {  
442          /*
443          String name = (String)e.getItem();
444          try {
445             Level level = Level.parse(value);
446             glob.changeLogLevel(name, level);
447          }
448          catch (XmlBlasterException ex) {
449             log.warning("Cannot set log level attribute '"+ name +"':" + ex.getMessage());
450          }
451          */
452          log.warning("is not implemented");
453       }
454    }
455 
456 
457    /**
458     * Create a Panel with a FillLevelBar and some labels.
459     * @param messageBar The instance of FillLevelBar to use
460     * @param totalCountLabel The instance of total count Label to use
461     * @param token Describing text e.g. "Published"
462     * @param gbc The layout manager
463     * @param offset The position of the panel (grid layout)
464     */
465    private void createBarPanel(FillLevelBar messageBar, Label totalCountLabel, String token, GridBagConstraints gbc, int offset)
466    {
467       final int LABEL_LOCATION_X = 2;
468       final int LABEL_LOCATION_Y = 10;
469       final int LABEL_WIDTH = 90;
470       final int LABEL_HEIGHT = 12;
471       final int BAR_X = 32;
472       final int BAR_Y = LABEL_LOCATION_Y + 2 * LABEL_HEIGHT;
473       final int BAR_WIDTH = 50;
474       final int BAR_HEIGHT = 130;
475       final int TOTAL_LABEL_Y = BAR_Y + BAR_HEIGHT;
476       final int PANEL_WIDTH = LABEL_LOCATION_X + LABEL_WIDTH + 2; // 94
477       final int PANEL_HEIGHT = TOTAL_LABEL_Y + LABEL_HEIGHT + 4;  // 180
478       Font barFont = new java.awt.Font("dialog", 2, 10);
479 
480       Panel panel = new Panel();
481       panel.setName(token + "MessagePanel");
482       panel.setLayout(null);
483       panel.setBackground(java.awt.SystemColor.control);
484       panel.setSize(PANEL_WIDTH, PANEL_HEIGHT);
485 
486       Label label1 = new Label();
487       label1.setName("Label1");
488       label1.setLocation(LABEL_LOCATION_X, LABEL_LOCATION_Y);
489       label1.setText(token);
490       label1.setBackground(java.awt.SystemColor.control);
491       label1.setSize(LABEL_WIDTH, LABEL_HEIGHT);
492       label1.setForeground(java.awt.Color.black);
493       label1.setFont(barFont);
494       label1.setAlignment(1);
495       panel.add(label1, label1.getName());
496 
497       Label label2 = new Label();
498       label2.setName("Label2");
499       label2.setLocation(LABEL_LOCATION_X, LABEL_LOCATION_Y + LABEL_HEIGHT);
500       label2.setText("[messages/sec]");
501       label2.setBackground(java.awt.SystemColor.control);
502       label2.setSize(LABEL_WIDTH, LABEL_HEIGHT);
503       label2.setForeground(java.awt.Color.black);
504       label2.setFont(barFont);
505       label2.setAlignment(1);
506       panel.add(label2, label2.getName());
507 
508       messageBar.setName(token + "Messages");
509       messageBar.setLocation(BAR_X, BAR_Y);
510       messageBar.setBackground(java.awt.SystemColor.control);
511       messageBar.setSize(BAR_WIDTH, BAR_HEIGHT);
512       boolean useAvg = true;
513       boolean isVariable = true;     // !!!
514       int MAX_SCALE = 10;
515       messageBar.init(0, 5, MAX_SCALE, Color.green, Color.green, useAvg, isVariable);
516       messageBar.setAvgValue(0);
517       // messageBar.init(0, 5, 100, Color.yellow, Color.green, true, true);
518       panel.add(messageBar, messageBar.getName());
519 
520       totalCountLabel.setName(token + "Label");
521       totalCountLabel.setLocation(LABEL_LOCATION_X, TOTAL_LABEL_Y);
522       totalCountLabel.setText("Total:  0");
523       totalCountLabel.setBackground(java.awt.SystemColor.control);
524       totalCountLabel.setSize(LABEL_WIDTH, LABEL_HEIGHT);
525       totalCountLabel.setForeground(java.awt.Color.black);
526       totalCountLabel.setFont(barFont);
527       totalCountLabel.setAlignment(1);
528       panel.add(totalCountLabel, totalCountLabel.getName());
529 
530       gbc.gridx=offset;
531       add(panel, gbc);
532    }
533 
534 
535    /**
536     * Create Checkboxes to adjust the logging levels
537     * @return container with checkboxes
538     */
539    private Panel createLogLevelBoxes()
540    {
541       Panel container = new Panel();
542       container.setLayout(new GridLayout(1, 7));
543 
544       Checkbox error = new Checkbox("ERROR", null, true);
545       error.addItemListener(new LogLevelListener());
546       container.add(error);
547 
548       Checkbox warning = new Checkbox("WARN", null, true);
549       warning.addItemListener(new LogLevelListener());
550       container.add(warning);
551 
552       Checkbox info = new Checkbox("INFO", null, true);
553       info.addItemListener(new LogLevelListener());
554       container.add(info);
555 
556       if (true/*log.CALL*/) { // log.CALL=true/false: check for dead code elimination
557          Checkbox calls = new Checkbox("CALL", null, false);
558          calls.addItemListener(new LogLevelListener());
559          container.add(calls);
560       }
561 
562       if (true/*log.TIME*/) {
563          Checkbox time = new Checkbox("TIME", null, false);
564          time.addItemListener(new LogLevelListener());
565          container.add(time);
566       }
567 
568       if (true/*log.TRACE*/) {
569          Checkbox trace = new Checkbox("TRACE", null, false);
570          trace.addItemListener(new LogLevelListener());
571          container.add(trace);
572       }
573 
574       if (true/*log.DUMP*/) {
575          Checkbox dump = new Checkbox("DUMP", null, false);
576          dump.addItemListener(new LogLevelListener());
577          container.add(dump);
578       }
579       return container;
580    }
581 
582 
583    /**
584     * Access the query history.
585     */
586    private QueryHistory getQueryHistory()
587    {
588       if (queryHistory == null)
589         queryHistory = new QueryHistory();
590       return queryHistory;
591    }
592 
593 
594    /**
595     *  Invoke: <pre>jaco org.xmlBlaster.MainGUI</pre><br />
596     * to start xmlBlaster with a control panel
597     */
598    static public void main(String[] args)
599    {
600       ServerScope glob = new ServerScope(args);
601       Main.controlPanel = new MainGUI(glob, null);
602       Main.controlPanel.run();
603    }
604 
605 
606 
607 
608    /**
609     * Polls the state of xmlBlaster.
610     */
611    private class PollingThread extends Thread
612    {
613       private final int POLLING_FREQUENCY = 1000;  // sleep 1 sec
614       private final MainGUI mainGUI;
615 
616 
617       /**
618        **/
619       public PollingThread(MainGUI mainGUI)
620       {
621          this.mainGUI = mainGUI;
622          // !!! setPriority(Thread.MIN_PRIORITY);
623       }
624 
625 
626       /**
627        * Start the timeout thread.
628        **/
629       public void run()
630       {
631          Thread.currentThread().setName("XmlBlaster GUIPollingThread");
632          try {
633             if (log.isLoggable(Level.FINE)) log.fine("Starting poller");
634             while (true) {
635                sleep(POLLING_FREQUENCY);
636                mainGUI.pollEvent(POLLING_FREQUENCY);  // Todo: calculate the exact sleeping period
637             }
638          }
639          catch (Exception e) {
640             log.fine("PollingThread problem: "+ e.toString());
641             //e.printStackTrace();
642          }
643       }
644    }
645 
646 
647 
648    /**
649     * Handles return key when a XPath query is entered into the TextField.
650     */
651    private class XPathKeyListener implements KeyListener
652    {
653       /**
654        * Access XPath query string (event from KeyListener).
655        */
656       public final void keyPressed(KeyEvent ev)
657       {
658          switch (ev.getKeyCode())
659          {
660             case KeyEvent.VK_ENTER:
661                try {
662                   if (clientQuery == null) {
663                      clientQuery = new GuiQuery(xmlBlasterMain.getAuthenticate(), xmlBlasterMain.getXmlBlaster());
664                   }
665                   queryOutput.setText("");
666                   getQueryHistory().changedHistory(inputTextField.getText());
667                   MsgUnitRaw[] msgArr = clientQuery.get(inputTextField.getText());
668                   for (int ii=0; ii<msgArr.length; ii++) {
669                      /*
670                      UpdateKey updateKey = new UpdateKey();
671                      updateKey.init(msgArr[ii].getKey());
672                      queryOutput.append("### UpdateKey:\n" + updateKey.printOn().toString());
673                      */
674                      queryOutput.append("### XmlKey:\n" + msgArr[ii].getKey());
675                      queryOutput.append("\n");
676                      queryOutput.append("### Content:\n" + new String(msgArr[ii].getContent()) + "\n");
677                      queryOutput.append("======================================================\n");
678                   }
679                   if (msgArr.length == 0) {
680                      if (publishedMessages == 0L) {
681                         queryOutput.setText("\n");
682                         queryOutput.append ("   Sorry, no data in xmlBlaster.\n");
683                         queryOutput.append ("   Use a demo client to publish some.\n");
684                         queryOutput.append ("\n");
685                      }
686                      else
687                         queryOutput.setText("****** Sorry, no match ******");
688                   }
689                } catch(XmlBlasterException e) {
690                   log.severe("XmlBlasterException: " + e.getMessage());
691                }
692                break;
693             case KeyEvent.VK_DOWN:
694                displayHistory(getQueryHistory().getNext());
695                break;
696             case KeyEvent.VK_UP:
697                displayHistory(getQueryHistory().getPrev());
698                break;
699          }
700       }
701       public final void keyReleased(KeyEvent ev)
702       {
703       }
704       public final void keyTyped(KeyEvent ev)
705       {
706       }
707    }
708 
709 
710    /**
711     * Scrolling with key arrow up/down your last XPath queries.
712     * @param stmt The XPath stmt to display
713     */
714    private void displayHistory(String stmt)
715    {
716       if (stmt.length() < 1) return;
717       inputTextField.setText(stmt);
718    }
719 
720 
721    /**
722     * A client accessing xmlBlaster to do some XPath query.
723     */
724    private class GuiQuery
725    {
726       private String queryType = Constants.XPATH;
727       private StopWatch stop = new StopWatch();
728       /** Handle to login */
729       I_Authenticate authenticate;
730       /** The handle to access xmlBlaster */
731       private I_XmlBlaster xmlBlasterImpl;
732       /** The session id on successful authentication */
733       private String secretSessionId;
734       private AddressServer addressServer;
735 
736       /**
737        * Login to xmlBlaster and get a sessionId. 
738        */
739       public GuiQuery(I_Authenticate authenticate, I_XmlBlaster xmlBlasterImpl) throws XmlBlasterException
740       {
741          this.xmlBlasterImpl = xmlBlasterImpl;
742          this.authenticate = authenticate;
743          String loginName = glob.getProperty().get("__sys__GuiQuery.loginName", "__sys__GuiQuery");
744          String passwd = glob.getProperty().get("__sys__GuiQuery.password", "secret");
745          ConnectQos connectQos = new ConnectQos(authenticate.getGlobal());
746          connectQos.loadClientPlugin("htpasswd", "1.0", loginName, passwd);
747          connectQos.getSessionQos().setSessionTimeout(0L);
748          // TODO: Port to use "LOCAL" protocol to connect
749          this.addressServer = new AddressServer(authenticate.getGlobal(), "NATIVE", authenticate.getGlobal().getId(), (java.util.Properties)null);
750          String ret = authenticate.connect(addressServer, connectQos.toXml(), null); // synchronous access only, no callback.
751          ConnectReturnQos retQos = new ConnectReturnQos(authenticate.getGlobal(), ret);
752          this.secretSessionId = retQos.getSecretSessionId();
753          log.info("login for '" + loginName + "' successful.");
754       }
755 
756       /**
757        * Query xmlBlaster.
758        */
759       MsgUnitRaw[] get(String queryString)
760       {
761          try {
762             GetKey getKey = new GetKey(this.authenticate.getGlobal(), queryString, queryType);
763             stop.restart();
764             MsgUnitRaw[] msgArr = this.xmlBlasterImpl.get(this.addressServer, this.secretSessionId, getKey.toXml(), "<qos/>");
765             log.info("Got " + msgArr.length + " messages for query '" + queryString + "'" + stop.nice());
766             return msgArr;
767          } catch(XmlBlasterException e) {
768             log.severe("XmlBlasterException: " + e.getMessage());
769             return new MsgUnitRaw[0];
770          }
771       }
772 
773       /** Logout. */
774       void logout() {
775          try { this.authenticate.disconnect(this.addressServer, this.secretSessionId, null); } catch (XmlBlasterException e) { }
776       }
777    }
778 
779 
780    /**
781     * Implements a stack to hold the previous XPath queries.
782     */
783    private class QueryHistory
784    {
785      //private String ME = "QueryHistory";
786      private Vector stack = new Vector();
787      private int currentIndex = 0;
788 
789 
790      /**
791       * Constructs a history stack.
792       */
793      public QueryHistory()
794      {
795      }
796 
797      /**
798       * Add new statement into history.
799       */
800      public void changedHistory(String stmt)
801      {
802        if (stack.size() > 1) {
803          String last = (String)stack.elementAt(stack.size()-1);
804          if (last.equals(stmt)) return;
805        }
806        currentIndex = stack.size();
807        stack.addElement(stmt);
808      }
809 
810 
811      /**
812       * Access previous XPath query.
813       */
814      String getPrev()
815      {
816        if (stack.size() < 1) return "";
817        if (currentIndex > 0) currentIndex--;
818        return (String)stack.elementAt(currentIndex);
819      }
820 
821 
822      /**
823       * Access next XPath query.
824       */
825      String getNext()
826      {
827        if (stack.size() < 1) return "";
828        if (currentIndex < stack.size()-1) currentIndex++;
829        return (String)stack.elementAt(currentIndex);
830      }
831 
832 
833      /**
834       * Access last (the newest), sets current to last.
835       */
836      String getLast()
837      {
838        if (stack.size() < 1) return "";
839        currentIndex = stack.size() - 1;
840        return (String)stack.elementAt(currentIndex);
841      }
842 
843 
844      /**
845       * Access first (the oldest), sets current to first.
846       */
847      String getFirst()
848      {
849        if (stack.size() < 1) return "";
850        currentIndex = 0;
851        return (String)stack.elementAt(currentIndex);
852      }
853 
854    }
855 }


syntax highlighted by Code2HTML, v. 0.9.1