001/*
002 * CDDL HEADER START
003 *
004 * The contents of this file are subject to the terms of the
005 * Common Development and Distribution License, Version 1.0 only
006 * (the "License").  You may not use this file except in compliance
007 * with the License.
008 *
009 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
010 * or http://forgerock.org/license/CDDLv1.0.html.
011 * See the License for the specific language governing permissions
012 * and limitations under the License.
013 *
014 * When distributing Covered Code, include this CDDL HEADER in each
015 * file and include the License file at legal-notices/CDDLv1_0.txt.
016 * If applicable, add the following below this CDDL HEADER, with the
017 * fields enclosed by brackets "[]" replaced with your own identifying
018 * information:
019 *      Portions Copyright [yyyy] [name of copyright owner]
020 *
021 * CDDL HEADER END
022 *
023 *
024 *      Copyright 2009 Sun Microsystems, Inc.
025 *      Portions Copyright 2014-2015 ForgeRock AS
026 */
027package org.opends.guitools.controlpanel.ui;
028
029import static org.opends.messages.AdminToolMessages.*;
030
031import java.awt.Component;
032import java.awt.Dimension;
033import java.awt.GridBagConstraints;
034import java.awt.GridBagLayout;
035import java.awt.Insets;
036import java.awt.Window;
037import java.util.HashMap;
038
039import javax.swing.ImageIcon;
040import javax.swing.JPanel;
041import javax.swing.JScrollPane;
042import javax.swing.JSplitPane;
043import javax.swing.JTree;
044import javax.swing.SwingUtilities;
045import javax.swing.border.EmptyBorder;
046import javax.swing.event.TreeSelectionEvent;
047import javax.swing.event.TreeSelectionListener;
048import javax.swing.tree.DefaultMutableTreeNode;
049import javax.swing.tree.DefaultTreeModel;
050import javax.swing.tree.TreePath;
051
052import org.opends.guitools.controlpanel.browser.IconPool;
053import org.opends.guitools.controlpanel.datamodel.ControlPanelInfo;
054import org.opends.guitools.controlpanel.datamodel.CustomSearchResult;
055import org.opends.guitools.controlpanel.datamodel.ServerDescriptor;
056import org.opends.guitools.controlpanel.event.ConfigurationChangeEvent;
057import org.opends.guitools.controlpanel.ui.components.TreePanel;
058import org.opends.guitools.controlpanel.ui.nodes.GeneralMonitoringTreeNode;
059import org.opends.guitools.controlpanel.ui.renderer.TreeCellRenderer;
060import org.opends.guitools.controlpanel.util.Utilities;
061import org.opends.guitools.controlpanel.util.ViewPositions;
062import org.forgerock.i18n.LocalizableMessage;
063import org.forgerock.i18n.LocalizableMessageBuilder;
064
065/**
066 * The pane that is displayed when the user clicks on 'General Monitoring'.
067 *
068 */
069public class BrowseGeneralMonitoringPanel extends StatusGenericPanel
070{
071  private static final long serialVersionUID = 6462914563746678830L;
072
073  /**
074   * The panel containing the tree.
075   */
076  private TreePanel treePane;
077
078  private JScrollPane treeScroll;
079
080  private ServerDescriptor lastServer;
081
082  private String lastServerName;
083
084  private boolean ignoreSelectionEvents;
085
086  private LocalizableMessage NO_ELEMENT_SELECTED =
087    INFO_CTRL_PANEL_GENERAL_MONITORING_NO_ITEM_SELECTED.get();
088  private LocalizableMessage MULTIPLE_ITEMS_SELECTED =
089    INFO_CTRL_PANEL_MULTIPLE_ITEMS_SELECTED_LABEL.get();
090
091  /**
092   * The enumeration used to define the different static nodes of the tree.
093   *
094   */
095  protected enum NodeType
096  {
097    /**
098     * Root node.
099     */
100    ROOT,
101    /**
102     * System information node.
103     */
104    SYSTEM_INFORMATION,
105    /**
106     * Java information node.
107     */
108    JAVA_INFORMATION,
109    /**
110     * Work queue node.
111     */
112    WORK_QUEUE,
113    /**
114     * Entry caches node.
115     */
116    ENTRY_CACHES,
117    /**
118     * Database environment node.
119     */
120    DB_ENVIRONMENT
121  }
122
123  /**
124   * The panel displaying the informations about the selected node.
125   */
126  protected GeneralMonitoringRightPanel entryPane;
127
128  /**
129   * Default constructor.
130   *
131   */
132  public BrowseGeneralMonitoringPanel()
133  {
134    super();
135    createLayout();
136  }
137
138  /** {@inheritDoc} */
139  @Override
140  public boolean requiresBorder()
141  {
142    return false;
143  }
144
145  /** {@inheritDoc} */
146  @Override
147  public boolean requiresScroll()
148  {
149    return false;
150  }
151
152  /** {@inheritDoc} */
153  @Override
154  public boolean callConfigurationChangedInBackground()
155  {
156    return true;
157  }
158
159  /** {@inheritDoc} */
160  @Override
161  public void toBeDisplayed(boolean visible)
162  {
163    Window w = Utilities.getParentDialog(this);
164    if (w instanceof GenericDialog)
165    {
166      ((GenericDialog)w).getRootPane().setDefaultButton(null);
167    }
168    else if (w instanceof GenericFrame)
169    {
170      ((GenericFrame)w).getRootPane().setDefaultButton(null);
171    }
172  }
173
174  /**
175   * Creates the layout of the panel (but the contents are not populated here).
176   */
177  private void createLayout()
178  {
179    setBackground(ColorAndFontConstants.greyBackground);
180    GridBagConstraints gbc = new GridBagConstraints();
181    gbc.anchor = GridBagConstraints.WEST;
182    gbc.gridx = 0;
183    gbc.gridy = 0;
184    gbc.gridwidth = 1;
185    gbc.weightx = 1.0;
186    gbc.fill = GridBagConstraints.BOTH;
187    addErrorPane(gbc);
188
189    gbc.insets = new Insets(10, 0, 0, 0);
190    gbc.gridx = 0;
191    gbc.gridy ++;
192    gbc.weightx = 1.0;
193    gbc.weighty = 1.0;
194    gbc.fill = GridBagConstraints.BOTH;
195    gbc.gridwidth = 7;
196    add(createSplitPane(), gbc);
197  }
198
199  /** {@inheritDoc} */
200  @Override
201  public LocalizableMessage getTitle()
202  {
203    return INFO_CTRL_PANEL_GENERAL_MONITORING_TITLE.get();
204  }
205
206  /** {@inheritDoc} */
207  @Override
208  public Component getPreferredFocusComponent()
209  {
210    return treePane;
211  }
212
213  /** {@inheritDoc} */
214  @Override
215  public void okClicked()
216  {
217    // No ok button
218  }
219
220  /** {@inheritDoc} */
221  @Override
222  public GenericDialog.ButtonType getButtonType()
223  {
224    return GenericDialog.ButtonType.CLOSE;
225  }
226
227  /**
228   * Creates the browser right panel.
229   * @return the created browser right panel.
230   */
231  private GeneralMonitoringRightPanel createBrowserRightPanel()
232  {
233    return new GeneralMonitoringRightPanel();
234  }
235
236  private Component createSplitPane()
237  {
238    treePane = new TreePanel();
239
240    entryPane = createBrowserRightPanel();
241
242    JPanel p = new JPanel(new GridBagLayout());
243    p.setBackground(ColorAndFontConstants.background);
244    GridBagConstraints gbc = new GridBagConstraints();
245    gbc.gridx = 0;
246    gbc.gridy = 0;
247    gbc.gridwidth = 1;
248    gbc.anchor = GridBagConstraints.NORTHWEST;
249    gbc.fill = GridBagConstraints.BOTH;
250    gbc.weightx = 1.0;
251    gbc.weighty = 1.0;
252    Utilities.setBorder(treePane, new EmptyBorder(10, 0, 10, 0));
253    p.add(treePane, gbc);
254    treeScroll = Utilities.createScrollPane(p);
255
256    treePane.getTree().addTreeSelectionListener(new TreeSelectionListener()
257    {
258      /** {@inheritDoc} */
259      public void valueChanged(TreeSelectionEvent ev)
260      {
261        if (!ignoreSelectionEvents)
262        {
263          ignoreSelectionEvents = true;
264          updateEntryPane();
265          ignoreSelectionEvents = false;
266        }
267      }
268    });
269    JTree tree = treePane.getTree();
270    repopulateTree(tree, true);
271    tree.setRootVisible(true);
272    tree.setVisibleRowCount(20);
273    tree.expandPath(new TreePath(getRoot(tree)));
274    tree.setCellRenderer(new GeneralMonitoringTreeCellRenderer());
275    treeScroll.setPreferredSize(
276        new Dimension(treeScroll.getPreferredSize().width + 30,
277            3 * treeScroll.getPreferredSize().height));
278    entryPane.displayMessage(NO_ELEMENT_SELECTED);
279    entryPane.setBorder(getRightPanelBorder());
280    entryPane.setPreferredSize(
281        new Dimension(treeScroll.getPreferredSize().width * 2,
282            treeScroll.getPreferredSize().height));
283    JSplitPane pane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
284    pane.setOpaque(true); //content panes must be opaque
285    pane.setLeftComponent(treeScroll);
286    pane.setRightComponent(entryPane);
287    pane.setResizeWeight(0.0);
288    pane.setDividerLocation(treeScroll.getPreferredSize().width);
289    return pane;
290  }
291
292  /** {@inheritDoc} */
293  @Override
294  public void setInfo(ControlPanelInfo info)
295  {
296    super.setInfo(info);
297    treePane.setInfo(info);
298    entryPane.setInfo(info);
299  }
300
301  /** {@inheritDoc} */
302  public void configurationChanged(ConfigurationChangeEvent ev)
303  {
304    ServerDescriptor server = ev.getNewDescriptor();
305    if (serverChanged(server))
306    {
307      final boolean firstTimeCalled = lastServer == null;
308      lastServer = server;
309
310      SwingUtilities.invokeLater(new Runnable()
311      {
312        public void run()
313        {
314          String serverName = getServerName(lastServer);
315          // Repopulate the tree to display a root node with server information
316          if (!serverName.equals(lastServerName))
317          {
318            repopulateTree(treePane.getTree(), false);
319            lastServerName = serverName;
320          }
321          if (firstTimeCalled)
322          {
323            // Select the root
324            treePane.getTree().setSelectionInterval(0, 0);
325          }
326          else
327          {
328            // Reselect
329            updateEntryPane();
330          }
331        }
332      });
333    }
334    else
335    {
336      lastServer = server;
337    }
338
339    boolean displayErrorPane = false;
340    LocalizableMessage errorTitle = LocalizableMessage.EMPTY;
341    LocalizableMessage errorDetails = LocalizableMessage.EMPTY;
342    ServerDescriptor.ServerStatus status = server.getStatus();
343    if (status == ServerDescriptor.ServerStatus.STARTED)
344    {
345      if (!server.isAuthenticated())
346      {
347        LocalizableMessageBuilder mb = new LocalizableMessageBuilder();
348        mb.append(
349   INFO_CTRL_PANEL_AUTH_REQUIRED_TO_BROWSE_MONITORING_SUMMARY.
350   get());
351        mb.append("<br><br>").append(getAuthenticateHTML());
352        errorDetails = mb.toMessage();
353        errorTitle = INFO_CTRL_PANEL_AUTHENTICATION_REQUIRED_SUMMARY.get();
354
355        displayErrorPane = true;
356      }
357    }
358    else if (status == ServerDescriptor.ServerStatus.NOT_CONNECTED_TO_REMOTE)
359    {
360      LocalizableMessageBuilder mb = new LocalizableMessageBuilder();
361      mb.append(INFO_CTRL_PANEL_CANNOT_CONNECT_TO_REMOTE_DETAILS.get(
362          server.getHostname()));
363      mb.append("<br><br>").append(getAuthenticateHTML());
364      errorDetails = mb.toMessage();
365      errorTitle = INFO_CTRL_PANEL_CANNOT_CONNECT_TO_REMOTE_SUMMARY.get();
366      displayErrorPane = true;
367    }
368    else
369    {
370      errorTitle = INFO_CTRL_PANEL_SERVER_NOT_RUNNING_SUMMARY.get();
371      LocalizableMessageBuilder mb = new LocalizableMessageBuilder();
372      mb.append(
373          INFO_CTRL_PANEL_SERVER_MUST_RUN_TO_BROWSE_MONITORING_SUMMARY.
374          get());
375      mb.append("<br><br>");
376      mb.append(getStartServerHTML());
377      errorDetails = mb.toMessage();
378      displayErrorPane = true;
379    }
380    final boolean fDisplayErrorPane = displayErrorPane;
381    final LocalizableMessage fErrorTitle = errorTitle;
382    final LocalizableMessage fErrorDetails = errorDetails;
383    SwingUtilities.invokeLater(new Runnable()
384    {
385      /** {@inheritDoc} */
386      public void run()
387      {
388        errorPane.setVisible(fDisplayErrorPane);
389        if (fDisplayErrorPane)
390        {
391          updateErrorPane(errorPane, fErrorTitle,
392              ColorAndFontConstants.errorTitleFont, fErrorDetails,
393              ColorAndFontConstants.defaultFont);
394        }
395      }
396    });
397  }
398
399  /**
400   * Populates the tree.  Should be called only once since the tree in this
401   * panel is static.
402   * @param tree the tree to be repopulated.
403   * @param forceScroll whether the scroll must be reset or not.
404   */
405  private void repopulateTree(JTree tree, boolean forceScroll)
406  {
407    ignoreSelectionEvents = true;
408
409    ViewPositions pos = Utilities.getViewPositions(treeScroll);
410
411    ServerDescriptor server = null;
412    if (getInfo() != null)
413    {
414      server = getInfo().getServerDescriptor();
415    }
416    GeneralMonitoringTreeNode root;
417    if (server == null)
418    {
419      root =
420        new GeneralMonitoringTreeNode(
421            INFO_CTRL_PANEL_GENERAL_MONITORING_ROOT.get().toString(),
422            NodeType.ROOT,
423            true);
424    }
425    else
426    {
427      root =
428        new GeneralMonitoringTreeNode(
429            getServerName(server),
430            NodeType.ROOT,
431            true);
432    }
433
434    LocalizableMessage[] messages = getNodeMessages();
435    NodeType[] identifiers = getNodeTypes();
436    for (int i=0; i < messages.length; i++)
437    {
438      root.add(new GeneralMonitoringTreeNode(messages[i].toString(),
439          identifiers[i], false));
440    }
441
442    DefaultTreeModel model = new DefaultTreeModel(root);
443    tree.setModel(model);
444
445    Utilities.updateViewPositions(pos);
446    ignoreSelectionEvents = false;
447  }
448
449  /**
450   * Updates the right entry panel.
451   *
452   */
453  private void updateEntryPane()
454  {
455    ViewPositions pos = Utilities.getViewPositions(entryPane);
456    boolean canDisplayMonitorInformation = true;
457    if (getInfo() == null)
458    {
459      return;
460    }
461    ServerDescriptor server = getInfo().getServerDescriptor();
462    ServerDescriptor.ServerStatus status = server.getStatus();
463    if (status == ServerDescriptor.ServerStatus.STARTED)
464    {
465      if (!server.isAuthenticated())
466      {
467        canDisplayMonitorInformation = false;
468        entryPane.displayMessage(
469            INFO_CTRL_PANEL_AUTHENTICATION_REQUIRED_SUMMARY.get());
470      }
471    }
472    else
473    {
474      canDisplayMonitorInformation = false;
475      entryPane.displayMessage(
476          INFO_CTRL_PANEL_SERVER_NOT_RUNNING_SUMMARY.get());
477    }
478
479    if (canDisplayMonitorInformation)
480    {
481      TreePath[] paths = treePane.getTree().getSelectionPaths();
482      TreePath path = null;
483      if (paths != null && paths.length == 1)
484      {
485        path = paths[0];
486      }
487      if (path != null)
488      {
489        GeneralMonitoringTreeNode node =
490          (GeneralMonitoringTreeNode)path.getLastPathComponent();
491        NodeType type = (NodeType)node.getIdentifier();
492        switch (type)
493        {
494        case ROOT:
495          entryPane.updateRoot();
496          break;
497        case SYSTEM_INFORMATION:
498          entryPane.updateSystemInformation();
499          break;
500        case WORK_QUEUE:
501          entryPane.updateWorkQueue();
502          break;
503        case ENTRY_CACHES:
504          entryPane.updateEntryCaches();
505          break;
506        case DB_ENVIRONMENT:
507          entryPane.updateDBEnvironment();
508          break;
509        case JAVA_INFORMATION:
510          entryPane.updateJavaInformation();
511          break;
512        default:
513          throw new RuntimeException("Unknown node type: "+type);
514        }
515      }
516      else
517      {
518        if (paths != null && paths.length > 1)
519        {
520          entryPane.displayMessage(MULTIPLE_ITEMS_SELECTED);
521        }
522        else
523        {
524          entryPane.displayMessage(NO_ELEMENT_SELECTED);
525        }
526      }
527    }
528    Utilities.updateViewPositions(pos);
529  }
530
531  private DefaultMutableTreeNode getRoot(JTree tree)
532  {
533    return (DefaultMutableTreeNode)tree.getModel().getRoot();
534  }
535
536  private boolean serverChanged(ServerDescriptor desc)
537  {
538    boolean changed = false;
539    if (lastServer != null)
540    {
541      // Just compare the elements interesting for this panel
542      changed =
543        !desc.getBackends().equals(lastServer.getBackends());
544      if (!changed)
545      {
546        CustomSearchResult[] monitor1 =
547        {
548            lastServer.getEntryCachesMonitor(),
549            lastServer.getJvmMemoryUsageMonitor(),
550            lastServer.getRootMonitor(),
551            lastServer.getSystemInformationMonitor(),
552            lastServer.getWorkQueueMonitor()
553        };
554        CustomSearchResult[] monitor2 =
555        {
556            desc.getEntryCachesMonitor(),
557            desc.getJvmMemoryUsageMonitor(),
558            desc.getRootMonitor(),
559            desc.getSystemInformationMonitor(),
560            desc.getWorkQueueMonitor()
561        };
562        for (int i=0; i<monitor1.length && !changed; i++)
563        {
564          if (monitor1[i] == null)
565          {
566            changed = monitor2[i] != null;
567          }
568          else
569          {
570            changed = !monitor1[i].equals(monitor2[i]);
571          }
572        }
573      }
574    }
575    else
576    {
577      changed = true;
578    }
579    return changed;
580  }
581
582  private HashMap<Object, ImageIcon> hmImages = new HashMap<>();
583  {
584    NodeType[] identifiers = {
585        NodeType.ROOT,
586        NodeType.SYSTEM_INFORMATION,
587        NodeType.JAVA_INFORMATION,
588        NodeType.WORK_QUEUE,
589        NodeType.ENTRY_CACHES,
590        NodeType.DB_ENVIRONMENT
591    };
592    LocalizableMessage[] ocPaths = {
593        INFO_CTRL_PANEL_GENERAL_MONITORING_ROOT_TREE_NODE.get(),
594        INFO_CTRL_PANEL_SYSTEM_INFORMATION_TREE_NODE.get(),
595        INFO_CTRL_PANEL_JVM_MEMORY_USAGE_TREE_NODE.get(),
596        INFO_CTRL_PANEL_WORK_QUEUE_TREE_NODE.get(),
597        INFO_CTRL_PANEL_ENTRY_CACHES_TREE_NODE.get(),
598        INFO_CTRL_PANEL_DB_ENVIRONMENT_TREE_NODE.get()
599    };
600    for (int i=0; i<identifiers.length; i++)
601    {
602      hmImages.put(identifiers[i],
603          Utilities.createImageIcon(IconPool.IMAGE_PATH+"/"+ocPaths[i],
604              getClass().getClassLoader()));
605    }
606  }
607
608  private String getServerName(ServerDescriptor server)
609  {
610    String serverName = server.getHostname();
611    if (server.getAdminConnector() != null)
612    {
613      serverName +=":"+server.getAdminConnector().getPort();
614    }
615    return serverName;
616  }
617
618  /**
619   * Specific class used to render the nodes in the tree.  It uses specific
620   * icons for the nodes.
621   *
622   */
623  protected class GeneralMonitoringTreeCellRenderer extends TreeCellRenderer
624  {
625    private static final long serialVersionUID = -3390566664259441766L;
626
627    /** {@inheritDoc} */
628    @Override
629    public Component getTreeCellRendererComponent(JTree tree, Object value,
630        boolean isSelected, boolean isExpanded, boolean isLeaf, int row,
631        boolean hasFocus)
632    {
633      super.getTreeCellRendererComponent(tree, value, isSelected, isExpanded,
634          isLeaf, row, hasFocus);
635      setIcon(getIcon(value));
636      return this;
637    }
638
639    private ImageIcon getIcon(Object value)
640    {
641      ImageIcon icon = null;
642      if (value instanceof GeneralMonitoringTreeNode)
643      {
644        icon = hmImages.get(
645            ((GeneralMonitoringTreeNode)value).getIdentifier());
646      }
647      else
648      {
649        throw new RuntimeException("Unexpected tree node: "+value);
650      }
651      return icon;
652    }
653  }
654
655  /**
656   * Returns the labels of the nodes to be displayed.
657   * @return the labels of the nodes to be displayed.
658   */
659  protected LocalizableMessage[] getNodeMessages()
660  {
661    return new LocalizableMessage[] {
662      INFO_CTRL_PANEL_SYSTEM_INFORMATION.get(),
663      INFO_CTRL_PANEL_JAVA_INFORMATION.get(),
664      INFO_CTRL_PANEL_WORK_QUEUE.get(),
665      INFO_CTRL_PANEL_ENTRY_CACHES.get(),
666      INFO_CTRL_PANEL_DB_ENVIRONMENT.get()
667    };
668  }
669
670  /**
671   * Returns the node types to be displayed.
672   * @return the node types to be displayed.
673   */
674  protected NodeType[] getNodeTypes()
675  {
676    return new NodeType[] {
677        NodeType.SYSTEM_INFORMATION,
678        NodeType.JAVA_INFORMATION,
679        NodeType.WORK_QUEUE,
680        NodeType.ENTRY_CACHES,
681        NodeType.DB_ENVIRONMENT
682    };
683  }
684}
685