Tutorial

Ajqvue Plugin Basics

Sept. 25, 2016

Introduction


    The Ajqvue application supports a fully modular plugin architecture. The plugin architecture allows the application to dynamically load code from the installation's sub-directory, lib/plugin, as the program is initialized when first run. Any Java JAR file placed in that directory will be scanned for the proper contents that can be identified as a Ajqvue plugin. Once identified the Ajqvue application will attempt to add that plugin as one of the main tabs the user will see on the right hand side of the Ajqvue window as shown in Figure 1. As an alternative to loading plugins automatically from the plugin directory a user may use the Tools | Plugin Management interface to manually load plugins that thereafter will also be available for use. Once a plugin is loaded then it will have access to the Ajqvue public API. What this mean is the plugin may obtain connections to the database, manipulate data in tables or create custom evaluation routines to analyze and report database contents.

illustration 1

Figure 1. Ajqvue Main Interface


Plugin_Module


    The core aspect of the Ajqvue plugin module architecture is oriented around the abstract Plugin_Module class. This class implements the Plugin Module Interface which defines the needed routines, class methods, that are required for loading and running a plugin module. The code for the Plugin_Module is provided here and shown in Listing 1. below. From this code listing we see the Plugin_Module class has a default no argument constructor and several methods. All plugins which will extend this class should do no initialization work in the constructor and just allow the default super() to be called. The key initialization for a plugin should be allocated to the initPlugin() method. The initPlugin() method has two argument instances of Main_Frame and String which will not be discussed or used in this basic tutorial for plugins, but will be covered in more advanced plugin module article(s). Just as a note at this time, the Main_Frame argument instance will allow a plugin to gain access to Ajqvue's standard database Menu commands. Now from the listing one should see that the initPlugin() method is commented out for the Plugin_Module abstract class. This is because this will force you as a developer to implement this method in your plugin module class.

Code Listing 1: (Plugin_Module.java)

//=================================================================
//                    Plugin_Module Class
//=================================================================
//
//    This class provides the abstract framework for plugin classes
// to extends in order to properly function within the application.
//
//                  < Plugin_Module.java >
//
//=================================================================
// Copyright (C) 2016 Dana M. Proctor
// Version 1.2 09/25/2016
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version
// 2 of the License, or (at your option) any later version. This
// program is distributed in the hope that it will be useful, 
// but WITHOUT ANY WARRANTY; without even the implied warranty
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
// the GNU General Public License for more details. You should
// have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation,
// Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
// (http://opensource.org)
//
//=================================================================
// Revision History
// Changes to the code should be documented here and reflected
// in the present version number. Author information should
// also be included with the original copyright author.
//=================================================================
// Version 1.0 Production PluginModule Class.
//         1.1 Renamed to Plugin_Module Class.
//         1.2 Comment Changes for getPanel() Methods.
//             
//-----------------------------------------------------------------
//                 danap@dandymadeproductions.com
//=================================================================

package com.dandymadeproductions.ajqvue.plugin;

import java.util.ArrayList;
import javax.swing.ImageIcon;
import javax.swing.JComponent;
import javax.swing.JMenuBar;

/**
 *    The Plugin_Module class provides the abstract framework for
 * plugin classes to extends in order to properly function within
 * the application.
 * 
 * @author Dana M. Proctor
 * @version 1.2 09/25/2016
 */

public abstract class Plugin_Module implements PluginModuleInterface
{
   // Class Instances.
   //protected Main_Frame parent;
   protected String path_FileName;
   public String name;
   public String author;
   protected String version;
   protected String description;
   protected String category;
   protected int size;
   protected ImageIcon tabIcon;
   protected JMenuBar menuBar;
   protected JComponent toolBar;
   protected JComponent panel;

   //===========================================================
   // PluginModule Constructor
   //===========================================================

   public Plugin_Module()
   {
      // Just Initialize to a NULL condition.
      
      path_FileName = null;
      name = null;
      author = null;
      version = null;
      description = null;
      category = null;
      size = 0;
      tabIcon = null;
      menuBar = null;
      toolBar = null;
      panel = null;
   }
   
   //==============================================================
   // Class method to setup up your plugin.
   // OVERIDE THIS METHOD!
   //==============================================================

   /*
   public void initPlugin(Main_Frame mainFrame, String path)
   {
      // This is where the plugin should be initialized.
      parent = mainFrame;
   }
   */
   
   //==============================================================
   // Class methods to get/set the plugin's file name.
   //==============================================================
   
   public String getPath_FileName()
   {
      return path_FileName;
   }
   
   //==============================================================
   // Class method to get/set the plugin's name.
   // Interface requirement.
   //==============================================================

   public String getName()
   {
      return name;
   }
   
   //==============================================================
   // Class method to get/set the plugin's author.
   // Interface requirement.
   //==============================================================

   public String getAuthor()
   {
      return author;
   }
   
   //==============================================================
   // Class method to obtain the plugin's version number.
   // Interface requirement.
   //==============================================================

   public String getVersion()
   {
      return version;
   }
   
   //==============================================================
   // Class method to obtain the plugin's description.
   // Interface requirement.
   //==============================================================

   public String getDescription()
   {
      return description;
   }
   
   //==============================================================
   // Class method to obtain the plugin's category.
   // Interface requirement.
   //==============================================================

   public String getCategory()
   {
      return category;
   }
   
   //==============================================================
   // Class method to obtain the plugin's size.
   // Interface requirement.
   //==============================================================

   public int getSize()
   {
      return size;
   }
   
   //==============================================================
   // Class method to allow the collection of a image icon that
   // will be used as an identifier in the tab structure.
   //
   // NOTE: The tab icon should be no larger than 16 x 16.
   // Interface requirement.
   //==============================================================

   public ImageIcon getTabIcon()
   {
      return tabIcon;
   }
   
   //==============================================================
   // Class method to obtain the plugin's JMenuBar that can be
   // used to control various aspects of the modules functionality.
   // Interface requirement.
   //==============================================================

   public JMenuBar getMenuBar()
   {
      return menuBar;
   }
   
   //==============================================================
   // Class method to allow the collection of a JToolBar to be
   // used with the plugin module.
   // Interface requirement.
   //==============================================================

   public JComponent getToolBar()
   {
      return toolBar;
   }
   
   //==============================================================
   // Class method for returning a JComponent, JPanel or JFXPanel
   // for inclusion in the application's main tab. Interface
   // requirement.
   //==============================================================

   public JComponent getPanel()
   {
      return panel;
   }
   
   //==============================================================
   // Class method for being able to set the database tables, occurs
   // if the database is reloaded.
   // Interface requirement.
   //==============================================================

   public void setDBTables(ArrayList tableNames)
   {
      // Do what you will if you need database table names.
   }
   
   //==============================================================
   // Class method to allow the plugin to start activities back
   // up after a stop() sequence.
   // (USED FOR CONTROLLING THREADS)
   //==============================================================

   public void start()
   {
      // Do what you will to start again from stop.
   }
   
   //==============================================================
   // Class method to allow the plugin to temporarily stop 
   // activities that may then be started again.
   // (USED FOR CONTROLLING THREADS)
   //==============================================================

   public void stop()
   {
      // Do what you will to notify stop.
   }
   
   //==============================================================
   // Class method to allow the plugin to close activities pending
   // a closing of the application.
   //==============================================================
   
   public void shutdown()
   {
      // Do what you will to notify pending closing.
   }
}
					

The key aspect of the initPlugin() method that a developer should know is that it should create a custom JPanel or JFXPanel component that Ajqvue will use to load into its tabbed pane for your plugin. That panel will be what users will see when your plugin tab is selected from the Ajqvue's main interface window on the right hand side. The correlated companion class method in the Plugin_Module class is the getPanel() method. That method should return the JComponent that is created in the initPlugin() method.

    Other methods in the Plugin_Module class, allow the developer to return the name and an icon that will be used to identify a plugin's tab. The name will appear as the tooltip for the tab and the icon will become the insignia for the tab. The icon returned through the getTabIcon() method should be no larger than 14 x 14 pixels. Two of the other getter methods shown in the listing allows a plugin to create its own Menu and Toolbar that will be used in conjunction with the module. Several other getter methods are also provided that allow a plugin to provide various other information for consumption. The The last method that is included in the Plugin_Module class is the setDBTables() method. This routine allows for your plugin to get an updated reload of the database tables that may occur during the running of the Ajqvue application.

Basic Plugin Example


    A basic plugin module example outlined below is a completely functional module that returns the record, row, count for a selected table. We start out with the basic outline for a Plugin_Module class, called PluginModule that extends Ajqvue's as show in code Listing 2., and create a class to handle the JPanel component creation in the initPlugin() method. As the code below shows the PluginModule class has now added the tableRecordCountPanel class instance. That new instance is a type TableRecordCountPanel and extends the JPanel object from the Java API. The tableRecordCountPanel instance is created in the initPlugin() method and takes an argument of the database's tables in the form of a ArrayList. Here is the illustration of a module using the Ajqvue public API to access the table names from Ajqvue's ConnectionManager class.

Code Listing 2: (Table Record Count PluginModule.java)

//=================================================================
//            TableRecordCount PluginModule Class
//=================================================================
//
//    This class provides the hook to incorporate a external plugin
// module into the Ajqvue application.
//
//                  < PluginModule.java >
//
//=================================================================
// Copyright (C) 2016 Dana M. Proctor
// Version 1.1 09/24/2016
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version
// 2 of the License, or (at your option) any later version. This
// program is distributed in the hope that it will be useful, 
// but WITHOUT ANY WARRANTY; without even the implied warranty
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
// the GNU General Public License for more details. You should
// have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation,
// Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
// (http://opensource.org)
//
//=================================================================
// Revision History
// Changes to the code should be documented here and reflected
// in the present version number. Author information should
// also be included with the original copyright author.
//=================================================================
// Version 1.0 Production TableRecordCount PluginModule.
//         1.1 Updated Reference to Ajqvue PluginModule to Plugin_Module.
//
//-----------------------------------------------------------------
//                 danap@dandymadeproductions.com
//=================================================================

package com.dandymadeproductions.tablerecordcount;

import java.util.ArrayList;

import javax.swing.ImageIcon;
import javax.swing.JPanel;

import com.dandymadeproductions.ajqvue.datasource.ConnectionManager;
import com.dandymadeproductions.ajqvue.gui.Main_Frame;
import com.dandymadeproductions.ajqvue.plugin.Plugin_Module;

/**
 *    The PluginModule class provides the hook to incorporate a external plugin
 * module into the Ajqvue application.
 * 
 * @author Dana M. Proctor
 * @version 1.1 09/24/2016
 */

public class PluginModule extends Plugin_Module
{
   // Class Instances
   private String pluginName, pluginAuthor;
   private TableRecordCountPanel tableRecordCountPanel;

   //==============================================================
   // PluginModule Constructor.
   //==============================================================

   public PluginModule()
   {
      super();
   }

   //==============================================================
   // Class method to initialize your plugin.
   //==============================================================

   public void initPlugin(Main_Frame parentFrame, String path)
   {
      pluginName = "Table Record Count";
      pluginAuthor = "Dandy Made Productions";
      tableRecordCountPanel = new TableRecordCountPanel(path, ConnectionManager.getTableNames());
   }

   //==============================================================
   // Class method to meet the interface requirements for getting
   // the name of the module.
   //==============================================================

   public String getName()
   {
      return pluginName;
   }
   
   //==============================================================
   // Class method to meet the interface requirements for getting
   // the author of the module.
   //==============================================================

   public String getAuthor()
   {
      return pluginAuthor;
   }
   
   //==============================================================
   // Class method to return the version release number of the
   // plugin module.
   //==============================================================
   
   public String getVersion()
   {
      return TableRecordCountPanel.getVersion();
   }
   
   //==============================================================
   // Class method to meet the interface requirements of returning
   // a ImageIcon that will be used as the plugin's tab Icon.
   //==============================================================

   public ImageIcon getTabIcon()
   {
      return tableRecordCountPanel.getTabIcon();
   }

   //==============================================================
   // Class method to meet the interface requirements for returning
   // a JPanel for inclusion in the Ajqvue application's main
   // tab.
   //==============================================================

   public JPanel getPanel()
   {
      return tableRecordCountPanel;
   }

   //==============================================================
   // Class method to meet the interface requirements for being
   // able to set the database tables.
   //==============================================================

   public void setDBTables(ArrayList tableNames)
   {
      tableRecordCountPanel.reloadPanel(tableNames);
      tableRecordCountPanel.repaint();
   }
}
					

The rest of the PluginModule class is similar to the Ajqvue Plugin_Module class with the overiden methods getName() and getPanel() returning to the Ajqvue application the String "Table Record Counts" for the tooltip in the plugin's tab and of course the tableRecordCountPanel that will be seen. The last method setDBTables() in the class reloads the database's table names via a class method, reloadPanel(), in the TableRecordCountPanel class.

    In proceeding with the discussion of a basic plugin module we now look in depth at the TableRecordCountPanel class. Instead of showing the listing in length for that class the major elements are described as required. All resources for the discussion in this article are made available via links at the end. Any classes given as part of this tutorial are covered by the GPL and may be used in accordance with that license.

As indicated earlier the TableRecordCountPanel class extends the JPanel Java API and in addition implements the ActionListener interface. By extending the JPanel class from Java we inherit all the aspects of a panel object needed as required by the Plugin_Module class and with the ActionListener we enable the ability to process events. The TableRecordCountPanel class instance definitions and constructor are shown in Listing 3. below. The class defines several instances that will be used to display and process record counts for tables in the database. The main class instance defined is a JComboBox called tableSelectionComboBox. This component will be used to select the name of the table to have its records counted. A second object is a JLabel that will be used to indicate the result of a record count. An additional class instance will be used to disable the processing of events when a record count is processing or table names are reloaded and is called disableActions.

Code Listing 3: (Constructor TableRecordCountPanel.java)

//=================================================================
//           TableRecordCount TableRecordCountPanel
//=================================================================
//
//    This class provides the panel that holds components
// associated with Ajqvue basic tutorial for a plugin module.
//
//               << TableRecordCountPanel.java >>
//
//=================================================================
// Copyright (C) 2016 Dana M. Proctor
// Version 1.0 09/21/2016
//
~
~
~
~
//=================================================================
// Version 1.0 Production TableFieldChartsPanel Class.
//                           
//-----------------------------------------------------------------
//                 danap@dandymadeproductions.com
//=================================================================

package com.dandymadeproductions.tablerecordcount;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Iterator;

import javax.swing.ImageIcon;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JPanel;

import com.dandymadeproductions.ajqvue.Ajqvue;
import com.dandymadeproductions.ajqvue.datasource.ConnectionManager;
import com.dandymadeproductions.ajqvue.gui.panels.DBTablesPanel;
import com.dandymadeproductions.ajqvue.gui.panels.TableTabPanel;
import com.dandymadeproductions.ajqvue.utilities.AResourceBundle;
import com.dandymadeproductions.ajqvue.utilities.Utils;

/**
 *    The TableRecordCountPanel class provides the panel that holds
 * components associated with the Ajqvue basic tutorial for a
 * plugin module.
 * 
 * @author Dana M. Proctor
 * @version 1.0 09/21/2016
 */

class TableRecordCountPanel extends JPanel implements ActionListener
{
   // Class Instances.
   private static final long serialVersionUID = 2500935698652883672L;
   
   private ImageIcon tabIcon;
   private JComboBox tableSelectionComboBox;
   private JLabel recordCountLabel;
   private boolean disableActions;
   
   private final static String VERSION = "Version 1.0";

   //==============================================================
   // TableRecordCountPanel Constructor
   //==============================================================

   TableRecordCountPanel(String path, ArrayList tableNames)
   {
      // Method Instances.
      AResourceBundle resourceBundle;
      String pathDirectory, localeDirectory, imagesDirectory, resource;
      JLabel tableNameLabel;
      
~
~
~

      // Create the components to select the database table,
      // identify the record count and the actual processed
      // row number.

      // Table Selector.
      tableSelectionComboBox = new JComboBox();
      tableSelectionComboBox.setEnabled(false);

      if (!tableNames.isEmpty())
      {
         Iterator tableNamesIterator = tableNames.iterator();
         while (tableNamesIterator.hasNext())
            tableSelectionComboBox.addItem(tableNamesIterator.next());

         tableSelectionComboBox.setEnabled(true);
      }
      tableSelectionComboBox.addActionListener(this);
      add(tableSelectionComboBox);

      // Identify Count.
      resource = resourceBundle.getResourceString("TableRecordCountPanel.label.RecordCount",
                                                  "Record Count");
      tableNameLabel = new JLabel(resource + ": ");
      add(tableNameLabel);

      // Record Count Value.
      recordCountLabel = new JLabel("0");
      
      add(recordCountLabel);
   }
~
~
					
				

Our constructor for the class does nothing more than create these components, assigned them default values, and add them to the panel. Note, the tableNames ArrayList argument in the constructor that was passed from the PluginModule and is used to fill the tableSelectionComboBox elements via a Iterator so that we isolate those elements from that ArrayList object.

Note: The actually constructor in the code contains some additional aspects that are used to derive resources for the class. These are not pertinent to the discussion of a basic plugin, but will be fully covered in the the advanced plugin turtorial.

Now in the constructor an ActionListener was added to the tableSelectionComboBox instance so events from that object selection changes could be captured and processed in the actionPerformed() method shown in Listing 4 below. As a user selects a table name from the pull down menu in that ComboBox a thread is created in the actionPerformed() method and forwards processing to the method exectueRecordCount(). In this method we do all the work for determining the record count for a table. First a connection is obtained from the Aqjvue ConnectionManager class public API getConnection(). Then a SQL statement object is created and the defined schema name for the selected database table is determined from the associated TableTabPanel in Ajqvue. A TableTabPanel is created for each table in the database and will hold valuable information about the table such as name, fields, keys, and other pertinent characteristics.

Code Listing 4: (Methods actionPerformed() & executeRecordCount() TableRecordCountPanel.java)

~
~
//==============================================================
   // ActionEvent Listener method for detecting the selection of
   // a table from the tableSelectionComboBox so that the row,
   // record count can be processed.
   //==============================================================

   public void actionPerformed(ActionEvent evt)
   {
      Object panelSource = evt.getSource();

      if (panelSource instanceof JComboBox && !disableActions)
      {
         Thread actionThread = new Thread(new Runnable()
         {
            public void run()
            {
               executeRecordCount();
            }
         }, "TableRecordCountPanel.actionThread");
         actionThread.start();
      }
   }
   
   //==============================================================
   // Class Method to execute the required processing the selected
   // database table to determine the record count.
   //==============================================================

   private void executeRecordCount()
   {
      // Setup Instances.
      Connection dbConnection;
      String tableName;
      String schemaTableName;

      String sqlStatementString;
      Statement sqlStatement;
      ResultSet rs;

      int rowCount = 0;
      tableSelectionComboBox.setEnabled(false);
      disableActions = true;

      // Get Connection to Database.

      dbConnection = ConnectionManager.getConnection("TableRecordCountPanel executeRecordCount()");

      try
      {
         // Create a statement object to be used with the connection.

         sqlStatement = dbConnection.createStatement();

         // Collect the selected table from the combobox.

         tableName = (String) tableSelectionComboBox.getSelectedItem();
         // System.out.println(tableName);

         // Collect the table name as referenced in the database.
         TableTabPanel selectedTablePanel = DBTablesPanel.getTableTabPanel(tableName);

         if (selectedTablePanel == null)
            return;

         tableName = selectedTablePanel.getTableName();
         // System.out.println(tableName);

         // Collect the properly formatted schema table name with identifier
         // quotes for execution in a SQL statement. 
         
         schemaTableName = Utils.getSchemaTableName(tableName);
         // System.out.println(schemaTableName);

         // Setup the statement string and execute the database query.

         sqlStatementString = "SELECT COUNT(*) FROM " + schemaTableName;
         rs = sqlStatement.executeQuery(sqlStatementString);
         rs.next();
         rowCount = rs.getInt(1);

         // Set the record count label to reflect the results
         // and close out.

         recordCountLabel.setText(Integer.toString(rowCount));

         rs.close();
         sqlStatement.close();
      }
      catch (SQLException e)
      {
         ConnectionManager.displaySQLErrors(e, "TableRecordCountPanel executeRecordCount()");
         recordCountLabel.setText("0");
         tableSelectionComboBox.setEnabled(true);
         disableActions = false;
      }
      
      // Close connection to database.
      
      ConnectionManager.closeConnection(dbConnection, "TableRecordCountPanel executeRecordCount()");
      tableSelectionComboBox.setEnabled(true);
      disableActions = false;
   }
~
~
					

With all the needed information for creating the required SQL statement with regard to collecting a count of the rows in the table we execute the query and obtain the resultset then store the value in the instance rowCount. Now all that is required is to assign the value to the recordCountLabel and close up.

    The final detail of the functional plugin example has to do with the reloading of the table names if changes occur in the database. A user could possibly change the characteristics of a table in the database via a import of a SQL statement or statements. Ajqvue if told will reload the database and through the Plugin_Module class method setDBTables() which will in turn send this information to your plugin. The code Listing 5 shows the TableRecordCountPanel class method reloadPanel() that is called from the PluginModule code discussed early in code Listing 3. In this method's argument is passed the reloaded table names from Ajqvue. The method essentially just removes all the elements in the tableSelectionComboBox then populates it with the new table names.

Code Listing 5: (Method reloadPanel() TableRecordCountPanel.java)

~
~
   //==============================================================
   // Class Method to reset the panel's table selector comboBox
   // if Ajqvue is called to reload the database tables.
   //==============================================================

   protected void reloadPanel(ArrayList tableNames)
   {
      // Insure no actions are taken during reload.

      tableSelectionComboBox.setEnabled(false);
      disableActions = true;

      // Clear the components of old data.

      tableSelectionComboBox.removeAllItems();
      recordCountLabel.setText("0");

      // Try reloading tables.
      if (!tableNames.isEmpty())
      {
         Iterator tableNamesIterator = tableNames.iterator();
         while (tableNamesIterator.hasNext())
         {
            tableSelectionComboBox.addItem(tableNamesIterator.next());
         }
         tableSelectionComboBox.setEnabled(true);
      }
      disableActions = false;
   }
~
~
					

Summary


    In this tutorial for Ajqvue Plugin Basics it has been discussed the general configuration needed to get a Ajqvue plugin installed via placement of a JAR file in the lib/plugin directory or via the Tools | Plugin Management menu. The main class that Ajqvue will look for in the JAR file needs to be called PluginModule and must follow the outline for the abstract class Plugin_Module shown in code Listing 1. A plugin must implement at least the initPlugin() method from the Plugin Module Interface. A basic extended Plugin_Module class was given in code Listing 2. that is a basic plugin example and was outlined via two classes, PluginModule and TableRecordCountPanel. This example Plugin Basics is a complete instance of an interaction with Ajqvue's public API such has getting a connection and calling on a TableTabPanel.


Resources

Plugin_Module Class
Functional PluginModule Class
TableRecordCountPanel Class