Java CAPS Tip : Building a JMS Browser Monitoring / Management Plug-in Module for NetBeans 6.1

This short Blog entry will take you through building a simple NetBeans Plug-in that can be used to browse the contents of a JMS Queue Manager. For the purpose of the Blog I will interface to the Java CAPS 6 Message Server although the example should be flexible enough to work with any Message Server assuming you have the appropriate interface. To interact with the Java CAPS Message Server this Blog entry will be built arounf the stcqueueviewer API but following on from this I have written the entry "NetBeans 6.1 Monitoring based on the Management API" that will highlight how we can modify it to use the Java CAPS Management API.

Contents

Module Description


The Module implemented within this blog will create a simple Java CAPS 6 JMS Servers node that will be integrated into the NetBeans Services tab in a similar way to the existing Servers node. The module will allow the user to specify and connect to multiple Sun-SeeBeyond JMS Servers and the associated configuration will be stored within the NetBeans <userdir>/config/JMSServer directory. Once connected to the appropriate Java CAPS 6 JMS Manager the user will be able to view all the Queues and Topics that are available within that Server and then for each Queue or Topic the Messages that are currently available.

This simple implement will provide the reader with the ability to build a JMS Monitoring / Management system that is integrated into the NetBeans framework.

Browser

Creating a Module Suite & Module


NetBeans provides a special Project Type that needs to be used to create and configure you module correctly. Therefore you will need to follow the Step below in order to create you JMS Message Server Browser Plug-in.
  1. Create a NetBeans Module Suite: File -> New Project ->NetBeans Modules -> Module Suite (Next)
    1. In the Project Name enter JMSBrowserSuite (Finish)
  2. Create a Plug-in Module: Right-click JMSBrowserSuite Modules -> New Module (Next)
    1. In the Name Type JMSBrowser. Leave the Standalone Module Option selected (Next)
    2. In the Basic Module Configuration Panel set the package name to org.sun.aph.nbm.jmsbrowser.
      Specify the location of the bundle and XML Layer file below com/sun/aph/nbm/jmsbrowser (Finish)

      NetBeans will now create the JMSBrowser Project and is ready to edit.
      Basic Project
  3. Create a new Jar Wrapper Module: Right-click JMSBrowserSuite Modules -> New Library (Next)
    1. Add the following Jars to the wrapper (multi Select) and they can be found in the <Java CAPS6 Root>/appserver/addons/stcms (Next)
      1. com.stc.jms.stcjms.jar
      2. com.stc.jms.stcqueueviewer
      3. jms.jar
    2. In the Name Type JMSBrowserSTCWrapper and leave all other fields to their default (Next)
    3. Change the "Code Name Base" to com.stc.jms.wrapper and the Module Display Name to JMS Browser STC Wrapper (Finish)

Module Dependencies

To facilitate the integration of the JMS Functionality into the Runtime Tab we will need to subclass several classes that belong to the NetBeans APIs. Each of these APIs needs to be defined as a project, module, dependency. In addition to this we will need to define the appropriate Sun SeeBeyond JMS Manager libraries as dependencies. This can be achieved through the Project Properties dialog box.
  1. Right-click on the project and select properties and the Properties Dialog will open.
  2. Within the Dialog select Libraries and add the following dependencies:
    1. Actions API
    2. Dialogs API
    3. File System API
    4. Nodes API
    5. Utilities API
    6. Windows System API
Module

Coding The Module


The meat of the module is based in its Java code and to successfully implement the functionality we will require the following distinct steps:

Creating the JMS Message Server Interface

To access the JMS Message Server we will need to create a number of accessor classes. Although this specific tutorial is aimed at connecting to the Java CAPS 6 Sun-SeeBeyond Message Server the interaction is based on Interface Definitions and therefore could be extended to integrate with other JMS Message Servers. The classes and interfaces we will need to create are described below.

Class / Interface
Description
Methods
MsgServerInterface
Defines the Methods required to access a Message Server.
  • connect()
  • disconnect()
  • isConnected()
  • getQueues()
  • getTopics()
  • getQueueStatistics()
  • getTopicStatistics()
QueueInterface
Defines the Methods required to access queue information
  • getQName()
  • setQName()
  • getServer()
  • setServer()
  • getMinSequence()
  • getMaxSequence()
  • getLastEnqueue()
  • getMsgCount()
  • getReceiverCount()
  • isSuspended()
  • refreshStatistics()
TopicInterface
Defines the Methods required to access topic information
  • getTName()
  • setTName()
  • getServer()
  • setServer()
  • getMinSequence()
  • getMaxSequence()
  • getLastEnqueue()
  • getMsgCount()
  • getReceiverCount()
  • isSuspended()
  • refreshStatistics()
MsgInterface
Defines the Methods for retrieving Message information

MsgServerFactory
Factory class used to retrieve the appropriate concrete MsgServer based on the supplied Server name. At present it will always return the Sun-SeeBeyond Server.
  • getMessageServer()
MsgServer
Represents a specific Message Server that we will connect to. Contains all the appropriate information host, port, username and password. Will control connection, disconnect and access to the server.

Queue
Java CAPS 6 specific implementation of the JMS Queue

Topic
Java CAPS 6 Specific implementation for a JMS Topic

Msg
Java CAPS 6 Specific implementation of the MsgInterface





MsgServer.java
  • connect () : Takes the specified connection parameters (host, port, username and password) and connects to the JMS Server
  • disconnect () : If the server is connected then disconnect.
  • isConnected () : Check if we are currently connected to the Message Server.
  • getQueues () : Returns a list of Queues, as QueueInterface, that exist on the Message Server.
  • getTopics () : Returns a list of Topics, as TopicInterface, that exist on the Message Server.
  • getQueueStatistics () : Get the Overall Queue Statistical information for a specified Queue.
  • getTopicStatistics () : Get the overall Topic statistical information for the specified Topic.
  • getQueueMessages () : Returns the messages on a specific queue as a List of MsgInterface.
  • getTopicMessages () : Returns the messages on a specific Topic as a List of MsgInterface.

Queue.java
  • getQName () : Returns the name of the queue
  • getServer () : Gets the Message Server the queue reside upon.
  • setServer () : Sets the server for this queue instance.
  • getMinSequence () : Get the current minimum message sequence number.
  • getMaxSequence () : Get the current maximum message sequence number.
  • getLastEnqueue () : Gets the Enqueue time of the last message written to the queue.
  • getMsgCount () : Get the number of available messages to be read.
  • getReceiverCount () Returns the number of connected receivers.
  • isSuspended () : Returns a boolean flag indicating if the queue is suspended.
  • refreshStatistics () : Refreshes the statistical information.

Topic.java
  • getQName () : Returns the name of the topic
  • getServer () : Gets the Message Server the topic resides upon.
  • setServer () : Sets the server for this topic instance.
  • getMinSequence () : Get the current minimum message sequence number.
  • getMaxSequence () : Get the current maximum message sequence number.
  • getLastEnqueue () : Gets the Enqueue time of the last message written to the queue.
  • getMsgCount () : Get the number of available messages to be read.
  • getReceiverCount () Returns the number of connected receivers.
  • isSuspended () : Returns a boolean flag indicating if the topic is suspended.
  • refreshStatistics () : Refreshes the statistical information.

Msg.java
  • setMessageProps () : Sets the messages properties as obtain from the server.
  • getSequenceNumber () : Gets the message Sequence Number
  • getMessageId () : Returns the Message Id
  • getStatus () : Returns the status of the message.
  • getMessageSize () : Returns the size in byte of the message.
  • getDeliveryMode () : Returns the Delivery Mode of the Message
  • getPriority () : Returns the Priority of the message.
  • getEnqueueTime () : Returns the Date / Time that the message was written to the Queue / Topic
  • getType () : Get the message type.

Creating the NetBeans Interface

Now that we have created the access class required to retrieve the Message Server information we need a framework within which this information will be displayed. To achieve this we will add an addition node to the Services Tab, similar to the Servers node, that will contain a list of all JMS Servers we want to connect to. The information for each of these Messages servers will be store within a properties file below the <UserDir>/config/JMSServers directory. BBy default the Server Nodes will not connect to their specified JMS Message Server but instead we will provide Connect / Disconnect Actions and a default (Preferred Action) Double-Click that will connect to the Server. Once connected tot he Server two specific Node types will be added to the Server. One of these will represent a Queue whilst the other represents a Topic. Again on these we will add a number of Actions and a Default (PreferredAction) that opens a display component to list all the messages on the Queue/Topic.

In this section we will examine the core java classes required to implement the NetBeans Module functionality and describe the key methods:
  • AllMsgServersNode.java
  • AllMsgServersChildren.java
  • AllMsgServersNotifier.java
  • NewServerPanel.java
  • OneMsgServerNode.java
  • OneMsgServerChildren.java
  • OneMsgServerNotifier.java
  • OneQueueNode.java
  • OneTopicNode.java
  • MsgListTopComponent.java
    • MsgListTopComponentSettings.xml
    • MsgListTopComponentWstcref.xml
  • RefreshAllMsgServersAction.java
  • RefreshMsgServerAction.java

AllMsgServersNode.java

This Java Class extends the AbstractNode specifying what children will exist below the main node and uses the AllMsgServersChildren class to keep track of the list of child nodes. The Node takes care of things such as the context menu and associated actions.
  • Methods
    • getActions : Get the following list of Actions that will be displayed in the context Menu for the top level server node.
      • RefreshAllMsgServersAction : Action defined in the RefreshAllMsgServersAction.java file to simple refresh the list of child nodes.
      • OpenLocalExplorerAction : Permits the user to make a new Explorer window showing on the JMS Message Servers.
      • NewAction : Enables the creation of a new sub-node by initiating a call to getNewTypes. This is a System Action controlled by NetBeans.
    • getNewTypes : Returns a list of NewType objects when the NewAction Context menu is selected. The action provides the actual GUI that will be displayed to collect the required information. The GUI used to obtain the JMS Server information is a Disalog generated (using the DialogDisplayer and DialogDescriptor) from the NewServerPanel.java. Hence when the NewAction is selected the Server data will be collected and a single NewType will be returned with the following create() method which will show the dialog to collect the Server information and then if the user selects "Save" create a new msgserver.properties file in the <userdir>/config/JMSServers directory.

       @Override
      public void create() throws IOException {
      String title = bundle.getString("LBL_NewMsgServerDialogTitle");
      String msg = bundle.getString("MSG_NewMsgServerDialogMsg");

      NewServerPanel nsp = new NewServerPanel();
      Dialog d = DialogDisplayer.getDefault().createDialog(new DialogDescriptor(nsp,title,true,nsp.getButtonActionListener()));
      d.pack();
      d.setVisible(true);
      d = null;
      }

AllMsgServersChildren.java

This class is responsible for keeping track of the list Message Servers that exists below the Root node. When first asked for the list it will read all msgserver\*.properties files that exist below the <userdir>/config/JMSServer directory (previous created using Add Message Server) and asks the node implementation to keep track of the loaded properties files. This class extends the Children.keys class and therefore we do not need to explicitly keep track of the nodes. Instead we track the keys which represent the nodes. We define, within the implementation, how to create a node for each key.
  • Methods
    • createNodes : Called by the implementation whenever needs to construct a child node. It is passed the Server Properties as the key for which it will make a node and will return only a single node of type OneMsgServerNode.
    • refreshList : When called this method will read all the msgserver\*.properties files and load them into an ArrayList. Once complete the setKeys method will be called with the list enabling the sub-node to be displayed, one per Server, in an unsorted fashion.

       private void refreshList() {
      List keys = new ArrayList();
      Properties msgServerProp = new Properties();
      FileObject msgServerPropFile = null;
      FileObject jmsServerFolder = Repository.getDefault().getDefaultFileSystem().getRoot().getFileObject(bundle.getString("CONFIG_DirectoryName"));
      InputStream is = null;
      if (jmsServerFolder != null) {
      Enumeration e = jmsServerFolder.getData(false);
      while (e.hasMoreElements()) {
      msgServerPropFile = (FileObject) e.nextElement();
      System.out.println("\*\*\* APH-I1 : Message Server Properties Filename : " + msgServerPropFile.getNameExt());
      msgServerProp = new Properties();
      try {
      keys.add(JMSServerPropertyFileManager.loadProperties(msgServerPropFile));
      } catch (Exception ex) {
      System.out.println(ex.getMessage());
      }
      }
      }

      // Collections.sort(keys);
      setKeys(keys);
      }

OneMsgServerNode.java

This Java Class extends the AbstractNode specifying what children will exist below the AllMsgServersNode node and uses the OneMsgServerChildren class to keep track of the child nodes. The Node takes care of things such as the context menu and associated actions.
  • Methods
    • getActions : Get the following list of Actions that will be displayed in the context Menu for the top level server node.
      • ConnectAction : Executes the server.connect method for the associated Msg Server and then if connected changes the icon.
      • DisconnectAction : Executes the server.disconnect method for the associated Message Server and then changes the icon.
      • RefreshMsgServerAction : Action defined in the RefreshMsgServerAction.java file to simple refresh the list of child nodes.
      • OpenLocalExplorerAction : Permits the user to make a new Explorer window showing on the JMS Message Servers.
      • PropertiesAction : Enables the building and display of a properties sheet and is controlled by the execution of createSheet(). This is a System Action controlled by NetBeans.
    • getPreferredAction : This method provides the preferred Action to be executed when the user Double-Clicks on the node. In the case of the OneMsgServerNode this will be to connect to the underlying Message Server.
    • createSheet : Configures the look of the property sheet. This creates a list of properties that will be displayed within the property sheet. The createSheet method is not called until there is a need to display the list of properties. For each property stored against the Message Server we will create a new Inner class that is a custom property that extends either PropertySupport.ReadWrite or PropertySupport.ReadOnly each will provide a getValue and setValue that will access the underlying Message Server properties.

       protected Sheet createSheet() {
      Sheet sheet = super.createSheet();
      Sheet.Set props = sheet.get(Sheet.PROPERTIES);
      if (props == null) {
      props = Sheet.createPropertiesSet();
      sheet.put(props);
      }

      class NameProp extends PropertySupport.ReadWrite {

      public NameProp() {
      super("serverName", String.class,
      bundle.getString("PROP_JMSServerName"), bundle.getString("HINT_JMSServerName"));
      }

      public Object getValue() {
      return serverName;
      }

      public void setValue(Object val) {
      serverName = val.toString();
      OneMsgServerNotifier.changed();
      }
      }

      class ServerProp extends PropertySupport.ReadOnly {

      public ServerProp() {
      super("server", String.class,
      bundle.getString("PROP_JMSServer"), bundle.getString("HINT_JMSServer"));
      }

      public Object getValue() {
      return serverType;
      }

      public void setValue(Object val) {
      serverType = val.toString();
      OneMsgServerNotifier.changed();
      }
      }

      class HostProp extends PropertySupport.ReadWrite {

      public HostProp() {
      super("host", String.class,
      bundle.getString("PROP_JMSHost"), bundle.getString("HINT_JMSHost"));
      }

      public Object getValue() {
      return serverHost;
      }

      public void setValue(Object val) {
      serverHost = val.toString();
      OneMsgServerNotifier.changed();
      }
      }

      class PortProp extends PropertySupport.ReadWrite {

      public PortProp() {
      super("port", int.class,
      bundle.getString("PROP_JMSPort"), bundle.getString("HINT_JMSPort"));
      }

      public Object getValue() {
      return Integer.parseInt(serverPort);
      }

      public void setValue(Object val) {
      serverPort = val.toString();
      OneMsgServerNotifier.changed();
      }
      }

      class UsernameProp extends PropertySupport.ReadWrite {

      public UsernameProp() {
      super("username", String.class,
      bundle.getString("PROP_JMSUsername"), bundle.getString("HINT_JMSUsername"));
      }

      public Object getValue() {
      return serverUsername;
      }

      public void setValue(Object val) {
      serverUsername = val.toString();
      OneMsgServerNotifier.changed();
      }
      }

      class PasswordProp extends PropertySupport.ReadWrite {

      public PasswordProp() {
      super("password", String.class,
      bundle.getString("PROP_JMSPassword"), bundle.getString("HINT_JMSPassword"));
      }

      public Object getValue() {
      return serverPassword;
      }

      public void setValue(Object val) {
      serverPassword = val.toString();
      OneMsgServerNotifier.changed();
      }
      }

      props.put(new NameProp());
      props.put(new ServerProp());
      props.put(new HostProp());
      props.put(new PortProp());
      props.put(new UsernameProp());
      props.put(new PasswordProp());

      OneMsgServerNotifier.addChangeListener(listener = new ChangeListener() {

      public void stateChanged(ChangeEvent ev) {
      System.out.println("State Change " + ev.getSource());
      System.out.println("Port " + msgServerProp.getProperty(bundle.getString("KEY_MsgServerPort")));

      System.out.println("Children Node Count :" + children.getNodesCount());
      System.out.println("Properties : " + msgServerProp);
      saveChanges();

      }
      });
      return sheet;
      }

OnMsgServerChildren.java

This class is responsible for keeping track of all the Queues and Topics that exist within the selected Message Server. When first asked for the list it will call the getQueues() and getTopics() methods for the associated MsgServer class and ask the node implementation to keep track of them. The class extends the Children.keys class and therefore does not need to explicitly keep track of the node. On returning from this call the keys associated with the node are of two types QueueInterface and TopicInterface and therefore when creating the nodes we will need to take this into account.

  • Methods
    • createNodes : Called by the implementation whenever needs to construct a child node. It is passed either a QueueInterface or TopicInterface as the key for which it will make a node and will return only a single node of OneQueueNode or OneTopicNode.

       @Override
      protected Node[] createNodes(Object key) {
      Node[] nodes = new Node[0];
      if (key instanceof QueueInterface) nodes = new Node[]{new OneQueueNode((QueueInterface) key)};
      else if (key instanceof TopicInterface) nodes = new Node[]{new OneTopicNode((TopicInterface) key)};

      return nodes;
      }

    • refreshList : When called this method will read all the queues and topics from the message server and load them into an ArrayList. Once complete the setKeys method will be called with the list enabling the sub-node to be displayed, one per Queue / Topic, in an unsorted fashion.

       private void refreshList() {
      List keys = new ArrayList();
      if (server != null) {
      keys = server.getQueues();
      keys.addAll(server.getTopics());
      }

      setKeys(keys);

      }

OneQueueNode.java / OneTopicNode.java


Essentially these classes are the same except one will deal with a specific Queue whilst the other deals with a specific Topic. These Java Classes provide the AbstractNode implementation for a single Queue or Topic and the constructor takes a QueueInterface or TopicInterface as the parameters. Because these nodes will never contain any sub-node the constructors call super(Children.LEAF) to indicate to the NetBeans framework that this is the case.
  • Methods
    • getActions : Get the following list of Actions that will be displayed in the context Menu for the top level server node.
      • ViewAction : Displays a TopComponent that contains a list of all messages currently available on the the selected Queue / Topic
      • PropertiesAction : Enables the building and display of a properties sheet and is controlled by the execution of createSheet(). This is a System Action controlled by NetBeans.
    • getPreferredAction : This method provides the preferred Action to be executed when the user Double-Clicks on the node and will execute the ViewAction.
    • createSheet : Configures the look of the property sheet. This creates a list of properties that will be displayed within the property sheet. The createSheet method is not called until there is a need to display the list of properties. For each available property within the Queue / Topic it will create a new class of type PropertySupport.ReadOnly.

       protected Sheet createSheet() {
      Sheet sheet = super.createSheet();
      Sheet.Set props = sheet.get(Sheet.PROPERTIES);
      if (props == null) {
      props = Sheet.createPropertiesSet();
      sheet.put(props);
      }

      queue.refreshStatistics();

      props.put(new PropertySupport.Name(this));

      class SuspendedProp extends PropertySupport.ReadOnly {

      public SuspendedProp() {
      super("suspended", boolean.class,
      bundle.getString("PROP_JMSSuspended"), bundle.getString("HINT_JMSSuspended"));
      }

      public Object getValue() {
      return queue.isSuspended();
      }

      public void setValue(Object nue) {
      // System.setProperty(key, (String) nue);
      OneMsgServerNotifier.changed();
      }
      }

      class MinSeqProp extends PropertySupport.ReadOnly {

      public MinSeqProp() {
      super("minSeq", long.class,
      bundle.getString("PROP_JMSMinSeq"), bundle.getString("HINT_JMSMinSeq"));
      }

      public Object getValue() {
      return queue.getMinSequence();
      }

      public void setValue(Object nue) {
      // System.setProperty(key, (String) nue);
      OneMsgServerNotifier.changed();
      }
      }

      class MaxSeqProp extends PropertySupport.ReadOnly {

      public MaxSeqProp() {
      super("maxSeq", long.class,
      bundle.getString("PROP_JMSMaxSeq"), bundle.getString("HINT_JMSMaxSeq"));
      }

      public Object getValue() {
      return queue.getMaxSequence();
      }

      public void setValue(Object nue) {
      // System.setProperty(key, (String) nue);
      OneMsgServerNotifier.changed();
      }
      }

      class AvailableProp extends PropertySupport.ReadOnly {

      public AvailableProp() {
      super("available", long.class,
      bundle.getString("PROP_JMSAvailable"), bundle.getString("HINT_JMSAvailable"));
      }

      public Object getValue() {
      return queue.getMsgCount();
      }

      public void setValue(Object nue) {
      // System.setProperty(key, (String) nue);
      OneMsgServerNotifier.changed();
      }
      }

      class ReceiversProp extends PropertySupport.ReadOnly {

      public ReceiversProp() {
      super("receivers", long.class,
      bundle.getString("PROP_JMSReceivers"), bundle.getString("HINT_JMSReceivers"));
      }

      public Object getValue() {
      return queue.getReceiverCount();
      }

      public void setValue(Object nue) {
      // System.setProperty(key, (String) nue);
      OneMsgServerNotifier.changed();
      }
      }

      class LastEnqueueProp extends PropertySupport.ReadOnly {

      public LastEnqueueProp() {
      super("lastEnqueue", Date.class,
      bundle.getString("PROP_JMSLastEnqueue"), bundle.getString("HINT_JMSLastEnqueue"));
      }

      public Object getValue() {
      return queue.getLastEnqueue();
      }

      public void setValue(Object nue) {
      // System.setProperty(key, (String) nue);
      OneMsgServerNotifier.changed();
      }
      }

      props.put(new SuspendedProp());
      props.put(new MinSeqProp());
      props.put(new MaxSeqProp());
      props.put(new AvailableProp());
      props.put(new ReceiversProp());
      props.put(new LastEnqueueProp());

      OneMsgServerNotifier.addChangeListener(listener = new ChangeListener() {

      public void stateChanged(ChangeEvent ev) {
      System.out.println("Event :" + ev.toString());
      firePropertyChange("value", null, null);
      }
      });
      return sheet;
      }

MsgListTopComponent.java

This class is used to display the Messages that reside on either a Queue or a Topic. To created this class, and its associated files, we will create a New Window Component within the project. When this is done NetBeans will generate the skeleton classes required and and example action that can be used to initiate the display of the component. Because we will be calling this from both the OneQueueNode and the OneTopicNode we will not be using the generated Action but rather taking the code and implementing the Inner class ViewMsgAction.
  1. Right-Click com.sun.aph.nbm.jmsbrowser package and Select New -> Window Component
  2. Set Window Position as editor and uncheck the "Open on Application Start" (Next)

    New Component

  3. Set the Class Name Prefic to MsgList (Finish)

    New Component

This will create a number of files and we will now modify the MsgListTopComponent.java to display the data correctly. I will not go into details on how to build the swing component because reviewing the code within NetBeans will show you the required code. In stead I will list the methods that were modified from those created by the New Windows Component call.

  1. Create two Constructors as follows and remove the existing constructor.

     private MsgListTopComponent(QueueInterface queue) {
    this.queue = queue;
    initComponents();
    // setQName(NbBundle.getMessage(MsgListTopComponent.class, "CTL_MsgListTopComponent"));
    setName(queue.getQName());
    setDisplayName(queue.getQName());
    setToolTipText(NbBundle.getMessage(MsgListTopComponent.class, "HINT_MsgListTopComponent"));
    // setIcon(Utilities.loadImage(ICON_PATH, true));
    postInitComponents();
    }

    private MsgListTopComponent(TopicInterface topic) {
    this.topic = topic;
    initComponents();
    // setQName(NbBundle.getMessage(MsgListTopComponent.class, "CTL_MsgListTopComponent"));
    setName(topic.getTName());
    setDisplayName(topic.getTName());
    setToolTipText(NbBundle.getMessage(MsgListTopComponent.class, "HINT_MsgListTopComponent"));
    // setIcon(Utilities.loadImage(ICON_PATH, true));
    postInitComponents();
    }

  2. Replace the default getDefault() method with the following two implementations. These methods create the default Component that will be displayed within the edit area if one does not exist.

     public static synchronized MsgListTopComponent getDefault(QueueInterface queue) {
    // if (instance == null)
    MsgListTopComponent instance = new MsgListTopComponent(queue);
    System.out.println("Getting Default for " + queue);
    return instance;
    }

    public static synchronized MsgListTopComponent getDefault(TopicInterface topic) {
    // if (instance == null)
    MsgListTopComponent instance = new MsgListTopComponent(topic);
    System.out.println("Getting Default for " + topic);
    return instance;
    }

  3. Replace the default findInstance() method with the following two implementations. This static method is called from the ViewMsgAction Inner class defined within the OneQueueNode and the OneTopicNode and is used to locate any existing occurrence of the component. The net result is that for any given Queue / Topic name a single tab will be displayed within the Editor area. This is possible because we set the Name component of this Wiget to be the name of the Queue / Topic and this is a Unique Id that NetBeans will track and control. If the Queue / Topic has not be open previously then an instance will be created and returned from the call.

     public static synchronized MsgListTopComponent findInstance(QueueInterface queue) {
    TopComponent win = WindowManager.getDefault().findTopComponent(queue.getQName());
    if (win == null) {
    Logger.getLogger(MsgListTopComponent.class.getName()).warning(
    "Cannot find " + queue.getQName() + " component. It will not be located properly in the window system.");
    return getDefault(queue);
    }
    if (win instanceof MsgListTopComponent) {
    return (MsgListTopComponent) win;
    }
    Logger.getLogger(MsgListTopComponent.class.getName()).warning(
    "There seem to be multiple components with the '" + queue.getQName() +
    "' ID. That is a potential source of errors and unexpected behavior.");
    return getDefault(queue);
    }

    public static synchronized MsgListTopComponent findInstance(TopicInterface topic) {
    TopComponent win = WindowManager.getDefault().findTopComponent(topic.getTName());
    if (win == null) {
    Logger.getLogger(MsgListTopComponent.class.getName()).warning(
    "Cannot find " + topic.getTName() + " component. It will not be located properly in the window system.");
    return getDefault(topic);
    }
    if (win instanceof MsgListTopComponent) {
    return (MsgListTopComponent) win;
    }
    Logger.getLogger(MsgListTopComponent.class.getName()).warning(
    "There seem to be multiple components with the '" + topic.getTName() +
    "' ID. That is a potential source of errors and unexpected behavior.");
    return getDefault(topic);
    }

  4. Overide the getPreferedId() method with the following. This will return the preferred Id that the findInstance will be looking for.

     @Override
    protected String preferredID() {
    return getName();
    }

  5. Create the refreshData() method, as follows, and add into the postInitComponent() which you will need to create. Also link it into the Action associated with the refresh button.

     // Methods
    private void postInitComponents() {
    refreshData();
    }

    private void refreshData() {
    List<MsgInterface> msgList = new ArrayList<MsgInterface>();

    cbSuspended.setSelected(false);
    tfMinSeq.setText("");
    tfMaxSeq.setText("");
    tfAvailable.setText("");
    tfReceivers.setText("");
    tfLastEnqueue.setText("");

    System.out.println("Queue : " + queue);
    System.out.println("Topic : " + topic);

    for (int i = ((DefaultTableModel) tblMessages.getModel()).getRowCount(); i > 0; i--) {
    ((DefaultTableModel) tblMessages.getModel()).removeRow(0);
    }

    if (queue != null) {
    cbSuspended.setSelected(queue.isSuspended());
    tfMinSeq.setText(Long.toString(queue.getMinSequence()));
    tfMaxSeq.setText(Long.toString(queue.getMaxSequence()));
    tfAvailable.setText(Long.toString(queue.getMsgCount()));
    tfReceivers.setText(Long.toString(queue.getReceiverCount()));
    tfLastEnqueue.setText(queue.getLastEnqueue().toString());

    msgList = queue.getMessages();


    } else if (topic != null) {
    cbSuspended.setSelected(topic.isSuspended());
    tfMinSeq.setText(Long.toString(topic.getMinSequence()));
    tfMaxSeq.setText(Long.toString(topic.getMaxSequence()));
    tfAvailable.setText(Long.toString(topic.getMsgCount()));
    tfReceivers.setText(Long.toString(topic.getReceiverCount()));
    tfLastEnqueue.setText(topic.getLastEnqueue().toString());

    msgList = topic.getMessages();

    }

    String[][] tableData = new String[msgList.size()][MessagesTableModel.COLUMNS];

    Iterator<MsgInterface> msgItr = msgList.iterator();
    int rowCnt = 0;
    MsgInterface msg = null;
    int i = 0;

    while (msgItr.hasNext()) {
    msg = msgItr.next();
    i=0;
    tableData[rowCnt][i++] = msg.getSequenceNumber();
    tableData[rowCnt][i++] = msg.getMessageId();
    tableData[rowCnt][i++] = msg.getStatus();
    tableData[rowCnt][i++] = msg.getMessageSize();
    tableData[rowCnt][i++] = msg.getEnqueueTime();
    tableData[rowCnt][i++] = msg.getPriority();
    tableData[rowCnt][i++] = msg.getDeliveryMode();
    tableData[rowCnt][i++] = msg.getType();
    rowCnt++;
    }

    for (i = 0; i < tableData.length; i++) {
    Object[] row = tableData[i];
    ((DefaultTableModel) tblMessages.getModel()).addRow(row);
    }

    }

Within the OneQueueNode.java and the OneTopicNode.java the following Inner class will need to be created. This will define the ViewMsgAction that appears in the context menu.

 private class ViewMsgAction extends AbstractAction {

public ViewMsgAction() {
super(bundle.getString("ACTION_ViewMessages"));
}

public void actionPerformed(ActionEvent evt) {
TopComponent win = MsgListTopComponent.findInstance(queue);
win.setDisplayName(queue.getQName());
win.open();
win.requestActive();
}
}

AllMsgServersNotifier.java / OneMsgSeverNotifier.java

These Java classes manage routing events whenever their are changes to the appropriate nodes. The child node will register with the appropriate Notifier.

RefreshAllMsgServersAction.java / RefreshMsgServerAction.java

These Java classes provide Refresh actions to the AllMsgServersNode and OneMsgServerNode and appear as the "Refresh" option in the appropriate context menu. They force a refresh to occur updating the display of the information and refreshes the list of sub-nodes.

Creating the Supporting Files

Once we have successfully coded the main files we must specify how we would like our Module to impact the filesystem and what labels and text we want to display the the user. The layer.xml file and the Bundle.properties file are used for this.
  1. Add the following entry to the layer.xml file :

     <folder name="UI">
    <folder name="Runtime">
    <file name="com.sun.aph.nbm.jmsbrowser.AllMsgServersNode.instance"/>
    </folder>
    </folder>

  2. Add the following to the Bundle.properties file:

    CTL_MsgListAction=MsgList
    CTL_MsgListTopComponent=MsgList Window
    HINT_MsgListTopComponent=This is a MsgList window
    CONFIG_DirectoryName=JMSServers
    LBL_NewMsgServer=New Message Server
    LBL_NewMsgServerDialogTitle=New Message Server
    MSG_NewMsgServerDialogMsg=New Message Server Name
    KEY_MsgServerName=MsgServerName
    KEY_MsgServerHost=MsgServerHost
    KEY_MsgServerPort=MsgServerPort
    KEY_MsgServerUsername=MsgServerUsername
    KEY_MsgServerPassword=MsgServerPassword
    KEY_MsgServerType=MsgServerType
    KEY_MsgServerConfigFile=MsgServerConfigFile
    LBL_AllMsgServersNode=JMS Servers
    HINT_AllMsgServersNode=JMS Message Server
    ICON_AllMsgServersNode=com/sun/aph/nbm/jmsbrowser/icon/messageserver.png
    LBL_OneMsgServerNode=JMS Message Server
    HINT_OneMsgServerNode=Sun SeeBeyond JMS Message Server
    ICON_OneMsgServerNode=com/sun/aph/nbm/jmsbrowser/icon/messageserver.png
    ICON_OneMsgServerNodeDisconnected=com/sun/aph/nbm/jmsbrowser/icon/application_server.png
    ICON_OneMsgServerNodeConnected=com/sun/aph/nbm/jmsbrowser/icon/application_server_run.png
    LBL_RefreshMsgServer=Refresh
    LBL_RefreshAllMsgServers=Refresh
    ICON_OneQueueNode=com/sun/aph/nbm/jmsbrowser/icon/queue.png
    HINT_OneQueueNode=JMS Queue
    ICON_OneTopicNode=com/sun/aph/nbm/jmsbrowser/icon/topic.png
    HINT_OneTopicNode=JMS Topic
    PROP_JMSServerName=Name
    HINT_JMSServerName=Name of the Server Connection.
    PROP_JMSServer=Server
    HINT_JMSServer=JMS Message Server.
    PROP_JMSHost=Host
    HINT_JMSHost=JMS Message Server host name or IP Address.
    PROP_JMSPort=Port
    HINT_JMSPort=JMS Message Server port number.
    PROP_JMSUsername=Username
    HINT_JMSUsername=JMS Message Server Username.
    PROP_JMSPassword=Password
    HINT_JMSPassword=JMS Message Server Password.
    PROP_JMSSuspended=Suspended
    HINT_JMSSuspended=Flag to indicate if the queue/topic is suspended.
    PROP_JMSMinSeq=Minimum Sequence
    HINT_JMSMinSeq=Minimum Message Sequence Number.
    PROP_JMSMaxSeq=Maximum Sequence
    HINT_JMSMaxSeq=Maximum Message Sequence Number.
    PROP_JMSAvailable=Available Count
    HINT_JMSAvailable=Number of message available to be read.
    PROP_JMSReceivers=Receiver Count
    HINT_JMSReceivers=Number of Receivers.
    PROP_JMSLastEnqueue=Last Publish Date
    HINT_JMSLastEnqueue=Number of Receivers.
    ACTION_ViewMessages=View Messages
    ACTION_ServerConnect=Connect
    ACTION_ServerDisconnect=Disconnect

  3. In addition we will add a number of icons, 16 x 16, that will be displayed on the nodes. As long as the ICON_\* entries within the Bundle.properties files match the names (relative) of the icon files these will be displayed. The attached project contains the appropriate icon images used within this blog.

Compiling, Installing and Running your Module


Now that we have completed the module it is time to try it out. NetBeans provides the ability to test out a module without having to install it with the Development environment so we will use this to run the module and then when happy generate the NBM to be installed in the Development environment.
  1. In the Projects window Right-Click on the JMSBroswerSuite project and choose "Clean and Build" this will remove all compiled files and the test user directory created in the next step.
  2. In the Projects window Right-Click on the JMSBrowser project and select "Install/Reload in Target Platform".
    The module is built and installed in the target IDE / Platform. The target IDE/Platform opens so that we can try out our new Module. The default target IDE/Platform is the installation used by your current instance of the development IDE. When we run the module a temporay test user directory will be created below th JMSBrowserSuite build directory.
  3. In the IDE's Services Window (Ctrl-5) you will see a new "JMS Servers" Node. Selecting this will show that it is currently empty.
  4. Right-Click on the JMS Servers node and select "Add New Message Server". Fill in the resulting Dialog with the details of a Java CAPS 6 Message Server.

    Create Server

    Select "Save" and the server details will be added (created as a file in <userdir>/config/JMSServers) to the JMS Server node. If the node is not refreshed and displayed then Rick-Click and select Refresh.

    Create Server 2

  5. Right-Click (or double-click) on the newly created Message Server node and select Connect. This will connect tot he Java CAPS 6 Message server and once connected the icon will change. You can now expand the node and it will display all the Queues and Topics available on the Server.

    Expanded Server

  6. Right-Clicking one of the Queues / Topics and selecting Properties will display the summary information for the Queue / Topic.

    Queue Properties

  7. If we now double-click on a specific Queue / Topic the messages associated with it will be displayed within a new tab in the edit area of NetBeans.

    Browse Messages

  8. Finally if we Right-Click on the Newly created Server Node we can review and change the Properties associated with the Server and these will be changed within the backing file.

    Server Properties

  9. Once we are happy with the functionality of the Module we can close down the test IDE and build  the appropriate NBM files for distribution. This is done by Rick-Clicking the JMSBrowserSuite and select "Create NBMs" which will generate the appropriate NBMs that can then be installed into your development IDE.
This Blog entry starts demonstrates how to build a simple JMS Server Monitoring Plug-in Module for NetBeans. It is not complete and addition functionality can be added by the reader who wants to take it forward to a fully functional implementation. I will over time be adding the following and full intend to use it in my day to day job.
  • Add Queues
  • Delete Queues
  • View Messages
  • Send Messages
  • Remove Messages
  • Add Subscribers / Read Messages
  • Auto Refresh
  • Sun MQ Message Server Functionality.


Resources



Keywords
  • Java CAPS 6
  • Java Message Server JMS
    • Monitoring
    • Management
  • NetBeans Plug-in Tutorial
    • Nodes API
    • File System API
    • Dialog API


Comments:

hi dante,

this is something i was missing for a long time now and posted here

http://forums.sun.com/thread.jspa?threadID=5311464

had logged an enhancement request at openmq via openmq issue tracker.

i love to see someone from sun picking this up a little bit.

regards chris

Posted by Christian Brennsteiner on September 09, 2008 at 03:08 PM GMT #

THANK YOU!

Posted by sysprv on September 09, 2008 at 11:08 PM GMT #

Post a Comment:
Comments are closed for this entry.
About

As a member of the Oracle A-Team we specialise in enabling and supporting the Oracle Fusion Middleware communities.

Search

Archives
« July 2014
MonTueWedThuFriSatSun
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
   
       
Today