Intermodule Communications in NetBeans Platform: Open a TopComponent in one module from another TopComponent in another module through the use of Lookup API

In this blog, I’d like to share my experience on an implementation of a NetBeans Platform application. The content below is mostly based on the CRUD tutorial with some of my own modifications. The key idea is on how to use Lookup API for intermodule communications, specifically, for opening a TopComponent in one module from within another TopComponent in another different module without setting up a direct dependency between two of them. Observe the differences of how the Lookup API is used in the ContractTC and in the ContractSummaryTC. So, have fun!

The scenario for this blog is that:

  • I am developing a NetBeans Platform application containing one data class called Contract and three user-interface (UI) windows named: MainTC, ContractTC and ContractSummaryTC.
    • Contract is a POJO class that contains methods and a number of attributes describing a contract such as contract_number, contract_date, and so on. It is created in its own module named Contract module.
    • MainTC is an entry point of an application. Here, a user can specify whether to create a new contract, or to display information of an existing contract.
    • ContractTC is a user-interface window that contains Form fields (JTextFields) for a user to enter information of a new contract.
    • ContractSummaryTC is a user-interface window that contains Swing components (JTextFields) displaying information of an existing contract.
  • Each UI window is created as a TopComponent in its own module. In other words, MainTC is created in main_editor module, ContractTC is created in contract_editor module, and ContractSummaryTC is created in contract_summary_editor module.
  • A user can go from
    • MainTC to ContractTC for entering contract information to create a new contract.
    • MainTC to ContractSummaryTC for displaying an existing contract information including its payment plan based on contract_number provided by a user.
    • ContractTC to ContractSummaryTC for displaying information of the newly-created contract such as the contract itself and its payment plan.
  • ContractTC requires the last contract number from MainTC in order to generating a new contract number.
  • ContractSummaryTC requires a Contract class object from MainTC. The Contract class object is retrieved from a database by using the contract number specified by a user at the MainTC.
  • ContractSummaryTC requires a Contract class object from ContractTC. The Contract class object is a new contract created from information provided by a user in ContractTC.

Use Case #1: Create a new contract.

  1. In MainTC, a user clicks on the “Create a new contract” button.
  2. The system finds the last contract number in a database.
  3. The system generates a new contract number based on the last contract number found.
  4. The system opens the ContractTC window with a new contract number.
  5. In ContractTC window, the user enters all necessary information for a new contract.
  6. The user clicks on the “Save” button.
  7. Go To Use Case #2

Use Case #2: Display information of a newly-created contract.

  1. Continued from Use Case #1.
  2. The system derives a payment plan for the new contract based on user-provided information.
  3. The system opens the ContractSummaryTC window with contract information and its derived payment plan.

Use Case #3: Display information of an existing contract.

  1. In MainTC, a user provides a contract number, and click on the “Find contract” button.
  2. The system searches for a contract in a database based on the contract number.
  3. The system opens the ContractSummaryTC window with information of the contract retrieved from the database.

Implementation guidelines:

  • MainTC, ContractTC, and ContractSummaryTC are in separate modules. For a loosely-coupling reason, we will not set dependency on any pair of these modules. However, all of them have direct dependencies on the Contract module.
  • When creating a new contract, MainTC will add a new contract number into its lookup, and open the ContractTC window. Then, the ContractTC window has to look for the new contract number in the lookup of the MainTC, when it is opened.
  • When displaying an existing contract, MainTC will add a contract class object into its lookup, and open the ContractSummary window. In addition, the ContractSummary window will have to look for a contract class object in the lookup of the MainTC, when it is opened.
  • When displaying a newly-created contract, ContractTC will add a newly-created contract class into its lookup, and open the ContractSummary window. Moreover, the ContractSummary window will look for a contract class object in the lookup of the ContractTC, when it is opened.

MainTC implementation:

  1. Declare InstanceContent variable as an instance variable. The InstanceContent is a dynamic object class that allows modifications of the content in a lookup.

    private InstanceContent content;

  2. Instantiate the InstanceContent variable in the MainTC constructor.

    content = new InstanceContent();

  3. Put the InstanceContent variable into the lookup of the MainTC by adding the following line after the line above in the MainTC constructor.

    associateLookup(new AbstractLookup(content));

  4. In a method in MainTC class that contains a new contract number, add the following lines to add the new contract number to the dynamic object in the lookup and to open the ContractTC window.

    String contractNumber = generateANewContractNumber();
    //Open the ContractTC window where "ContractTC" is its Preferrred_ID.
    TopComponent tc = WindowManager.getDefault().findTopComponent("ContractTC");
    this.content.add(contractNumber);
    if (tc != null) {
       tc.open();
       tc.requestActive();
    }
    this.content.remove(contractNumber);

  5. In a method in MainTC that contains an existing contract class object, add the following lines to add the object to the lookup of MainTC and to open the ContractSummaryTC window.

    Contract contractObj = retrieveContractFromDatabase(contractNumber);
    TopComponent tc = WindowManager.getDefault().findTopComponent("ContractSummaryTC");
    if (tc != null) {
       tc.open();
       tc.requestActive();
    }
    this.content.add(contractObj);
    this.content.remove(contractObj);


    Note #1: I add the contract class object to the lookup of MainTC after I open the ContractSummaryTC window, and then remove the object from the lookup of MainTC. I do not know the reason, but if I add the contract class object to the lookup of MainTC before I open the ContractSummaryTC window, it does not work.
    Note #2: The above implementations are pretty much what we need to do for adding things to the lookup of a TopComponent (MainTC) in one module. So, other modules can use them.
    Note #3: In summary, we add a contract number as String data type and a contract object as Contract class type to the lookup of MainTC. The first is for ContractTC and the later is for ContractSummaryTC.

ContractTC implementation:

  1. Modify the ContractTC class signature to implement LookupListener as shown below.


    public final class ContractTC extends TopComponent implements LookupListener {
    ...
    ...
    }

  2. Declare a Contract object as an instance variable.

    Contract contractObj=null;

  3. Declare a Lookup.Result variable as an instance variable.

    private Lookup.Result result = null;

  4. Declare an InstanceContent variable as an instance variable.

    private InstanceContent content = null;

  5. Add DocumentListener to JTextFields for save functionality in the constructor.

    this.jTextFieldContractNumber.getDocument().addDocumentListener(new DocumentListener(){
      @Override
      public void insertUpdate(DocumentEvent e) {
        fire(true);
      }
      @Override
      public void removeUpdate(DocumentEvent e) {
        fire(true);
      }
      @Override
      public void changedUpdate(DocumentEvent e) {
        fire(true);
      }
    });

  6. Declare a SaveCookie variable as an instance variable.

    private SaveCookie impl = null;

  7. Instantiate the SaveCookie

    impl = new SaveCookieImpl();

  8. Instantiate the InstanceContent variable in the constructor

    content = new InstanceContent();

  9. Put the InstanceContent object into the lookup of the ContractTC.

    associateLookup(new AbstractLookup(content));

  10. Define an inner private class named SaveCookieImpl that implements SaveCookie interface.


    private class SaveCookieImpl implements SaveCookie {
      @Override
      @SuppressWarnings("unchecked")
      public void save() throws IOException {
      //Implement this method.
      }

  11. Implement the save() method in the SaveCookieImpl class.


    @Override
    @SuppressWarnings("unchecked")
    public void save() throws IOException {
      Confirmation message = new NotifyDescriptor.Confirmation("Do you want to save changes?",
    NotifyDescriptor.OK_CANCEL_OPTION, NotifyDescriptor.QUESTION_MESSAGE);
      Object result = DialogDisplayer.getDefault().notify(message);
      if (NotifyDescriptor.YES_OPTION.equals(result)) {
        fire(false);
        //Obtain contract information from user interface such as JTextFields.
        //Create a contract class object from the obtained information.
        contractObj=createContractObjFromObtainedData();
        //May save the contract class object into a database.
        //Open the ContractSummaryTC window below.
        TopComponent tc = WindowManager.getDefault().findTopComponent("ContractSummaryTC");
        if (tc != null) {
          tc.open();
          tc.requestActive();
        }
        content.add(contractObj);
        content.remove(contractObj);
      }
      else
      {
        javax.swing.JOptionPane.showMessageDialog(null, "Unsuccessfully add a new contract.");
      }
    }

  12. Implement the fire() method that is called in the save() method above.

    public void fire(boolean modified) {
      if (modified) {
        //If the text is modified,
        //we add SaveCookie impl to Lookup
          content.add(impl);
        } else {
        //Otherwise, we remove the SaveCookie impl from the lookup
          content.remove(impl);
        }
      }
    }

  13. Override the componentOpened() method as follows.

    result = WindowManager.getDefault().findTopComponent("MainTC").getLookup().lookupResult(String.class);
    result.addLookupListener(this);
    resultChanged(new LookupEvent(result));

  14. Override the componentClosed() method as follows.

    result.removeLookupListener(this);
    result = null;

  15. Override the componentDeactivated() method as follows.

    @Override
    protected void componentDeactivated() {
      this.content.remove(contractObj);
    }

  16. Override the resultChanged() method as follows.

    @Override
    @SuppressWarnings("unchecked")
    public void resultChanged(LookupEvent le) {
      Lookup.Result r = (Lookup.Result) le.getSource();
      Collection col = r.allInstances();
      if (!col.isEmpty()) {
        for (String contractNumber : col) {
          this.jTextFieldContractNumber.setText(contractNumber);
          //Do anything that you would like to.
        }
      }
    }

ContractSummaryTC implementation:

  1. Define two LookupListener classes as private inner classes. These two custom classes are used to detect changes of the content in the lookups of MainTC and ContractTC, respectively.


    private class MyMainTCLookupListener implements LookupListener {
      @Override
      @SuppressWarnings("unchecked")
      public void resultChanged(LookupEvent le) {
        Lookup.Result r = (Lookup.Result) le.getSource();
        Collection col = r.allInstances();
        if (!col.isEmpty()) {
         for (ContractT cont : col) {
          jTextFieldContractNumber.setText(cont.getContractNumber());
          jTextFieldContractDate.setText(cont.getContractDate());
          //Retrieve payment history, or
          //Do something else as you would like to.
         }
        }
       }
    }


    private class MyContractTCLookupListener implements LookupListener {
      @Override
      @SuppressWarnings("unchecked")
      public void resultChanged(LookupEvent le) {
        Lookup.Result r = (Lookup.Result) le.getSource();
        Collection col = r.allInstances();
        if (!col.isEmpty()) {
         for (ContractT cont : col) {
          jTextFieldContractNumber.setText(cont.getContractNumber());
          jTextFieldContractDate.setText(cont.getContractDate());
          //Generate a payment plan, or
          //Do something else as you would like to.
         }
        }
       }
    }

    Note: The reason I define two separate LookupListener classes is that I can differentiate where or which class the Contract object is coming from. For example, if a contract object is from MainTC, I know that the contract is retrieved from the database. Accordingly, I can further retrieve payment history for this contract. In contrary, if a contract object is from ContractTC, I know that it is a newly-created contract. So, I can further implement a generation of payment plan for this contract.

     

  2. Declare two LookupListener variables as instance variables.

    private MyMainTCLookupListener mainTCLookupListener;
    private MyContractTCLookupListener contractTCLookupListener;

  3. Declare two Lookup.Result variables as instance variables.

    private Lookup.Result mainTCLookupResult;
    private Lookup.Result contractTCLookupResult;

  4. Instantiate LookupListener variables in the constructor.


    mainTCLookupListener = new MyMainTCLookupListener();
    contractTCLookupListener = new MyContractTCLookupListener();

  5. Override the componentOpened() method as follows.

    mainTCLookupResult = WindowManager.getDefault().findTopComponent("MainTC").getLookup().lookupResult(ContractT.class);
    mainTCLookupResult.addLookupListener(mainTCLookupListener);
    contractTCLookupResult = WindowManager.getDefault().findTopComponent("ContractTC").getLookup().lookupResult(ContractT.class);
    contractTCLookupResult.addLookupListener(contractTCLookupListener);

  6. Override the componentClosed() method as follows.

    mainTCLookupResult.removeLookupListener(mainTCLookupListener);
    mainTCLookupResult = null;
    contractTCLookupResult.removeLookupListener(contractTCLookupListener);
    contractTCLookupResult = null;

Advertisements
Posted in Uncategorized | 2 Comments

Drupal 7 (RC2): How to create a customized front page.

In this blog, I briefly describe a procedure for creating a customized Drupal front page. It’s just a series of steps of what to do, not a detailed how-to.

  1. Get into the theme directory: $Drupal7_Home/sites/all/themes/danland, where “danland” is the theme name.
  2. Copy and paste page.tpl.php into the same directory as it already is.
  3. Rename the copied file to page--front.tpl.php. Notice that there are two dashes.
  4. Change whatever you’d like to do for your customization in the page--front.tpl.php. For example, for a testing purpose, I change the phrase “Theme by” at the bottom of the page to “Developed by”.
  5. Clear Drupal caches:
    1. Open a web browser and point the address to your Drupal homepage such as http://localhost/drupal7
    2. Log into your Drupal homepage as administrator.
    3. Click on “Configuration”
    4. Then, under the “Development” section, click on “Performance”
    5. Finally, click on “Clear all caches”.
  6. Refresh your Drupal homepage, the “Theme by” at the button of the homepage should be changed to “Developed by”.

Let’s try to customize Acquia Marina theme as I explain in this blog.

Posted in Uncategorized | 10 Comments

Fix incorrectly-displayed column names when creating a custom JTableModel that extends AbstractTableModel.

When we write a custom JTable that extends AbstractTableModel, its column names may not be displayed correctly if we forget to override a particular method. By the “incorrect display”, I mean that instead of showing names of the columns specified in the custom class, the column names were displayed as “A”, “B”, “C”, … 

To fix this problem, we need to implement getColumnName() in our custom TableModel. This method is not required for extending AbstractTableModel. So, some of us may not do it.

For example, if we define column names of a table as

String[] columnNames={"name1", "name2"};

Then, we can override the getColumnName(int columnIndex) as


public String getColumnName(int columnIndex){
return columnNames[columnIndex];
}

Well, do not get confused between:
getColumnNames() and getColumnName(int columnIndex).

Posted in Uncategorized | Leave a comment

How to enable clean URLs in Drupal 6.19

Drupal could be considered as one of the big three open-source Content Management Systems (CMSs), in addition to WordPress and Joomla. Its installation is simple. First, it requires a new blank database in MySQL with some proper privileges. Then, unzip the drupal downloaded file into a web root directory of a web server. In case of Apache HTTP server, the web root directory is the htdocs directory. Finally, make a copy of default.settings.php in the $Drupal_Home/sites/default directory and rename it to settings.php. Nothing much to do rather than some mouse clicks and a few typings.

Although the installation is not difficult, its configuration needs some patience and knowledge. One desired configuration is to enable clean URLs. There are various approaches posted on the Internet. Some of them work for some people, and some of them don’t.

So, in this post, I’d like to share what is worked for me. My system is:

  • Windows Vista (64-bit)
  • Apache HTTP Server (2.2.16 win32 x86 with ssl, msi installer)
  • PHP (5.3.3 win32 VC 9 x86)
  • MySQL (5.1.49, win32, noinstall version)
  • Drupal (6.19)

To install Drupal, I unzip (and rename) Drupal into $APACHE_HOME/htdocs directory, resulting in $Apache_Home/htdocs/drupal.

To enable clean URL,

1) Edit .htaccess in $PHP_HOME

Change the following line from:

#RewriteBase   /

to:

RewriteBase    /drupal

2) Edit httpd.conf in $APACHE_HOME/conf

Change the following line from:

#LoadModule rewrite_module modules/mod_rewrite.so

to:

LoadModule rewrite_module modules/mod_rewrite.so

, and then change this line from:

AllowOverride None

to:

AllowOverride All

Note: there are more than one AllowOverride None in httpd.conf file. The correct one is under the paragraph with .htaccess in it.

3) After saving those modifications above, restart MySQL and HTTP Server, open a browser, and point the browser to http://localhost/drupal. Next, click on “Administer” –> “Clean URLs”. Now, The “Enabled” bullet list should be active.

Hope this help save you guys’ time.

Posted in Uncategorized | Leave a comment

Greenstone 3 Installation on Window 7 (64-bit)

In this blog, I’d like to share my experience on installing Greenstone3, a digital library software for building and distributing digital library collections, in my 64-bit version of Windows 7 machine. My installation approach may certainly not be a good one, but it can get the software up and running.

Requirements

  • Sun JDK 1.6.0_17 (jdk-6u17-windows-i586.exe)
  • Apache Tomcat 6.0.26 (apache-tomcat-6.0.26-windows-x86.zip)
  • Greenstone3 (greenstone-3.04-win32.exe)
  • (Note: Although my machine and OS are 64-bit versions, the software above is all 32-bit).)

The installation procedure is divided into the following main steps.

  1. Install Sun JDK
  2. Install Apache Tomcat
  3. Install Greenstone3
  4. Configure Greenstone
  5. Configure Tomcat
  6. Run Greenstone3

1. Sun JDK Installation

  • Double click on the jdk-6u17-windows-i586.exe file to install it into the default directory, which is C:\Program Files (x86)\Java.
  • Set JAVA_HOME.
    • Control Panel > System and Security > System > Advanced System Settings > Environment Variables.
    • Under the System Variables, click the “New…” button.
    • Enter values for the following two variables: Variable name: JAVA_HOME, and Variable value: C:\Program Files (x86)\Java\Jdk1.6.0_17.
    • Click the OK button to complete the setup.
  • Add Java to the PATH environment.
    • Under the System Variables, click to highlight the “Path” variable.
    • Click the “Edit…” button.
    • Add %JAVA_HOME%\bin to the end of Path’s variable value.
    • Click the OK, OK and OK buttons to complete the setup.

2. Apache Tomcat Installation

  • Unzip the “apache-tomcat-6.0.26-windows-x86.zip” file into any directory
  • Copy the unzipped directory and paste it into C:\, resulting in C:\apache-tomcat-6.0.26 directory.
  • Set up Apache Tomcat home path
    • Go to the Environment Variables, using the same approach as used in setting JAVA_HOME above.
    • Click the “New…” button.
    • Enter values for the following two variables: Variable name: CATALINA_HOME, and Variable value: C:\apache-tomcat-6.0.26.
    • Click the OK, OK and OK buttons to complete the setup.
  • Test the Apache Tomcat installation
    • Open a web browser
    • Type: http://localhost:8080/ in the address bar.
    • The Apache Tomcat startup page should be displayed.

3. Greenstone3 Installation

  • Double click on the greenstone-3.04-win32.exe file.
  • Install it into C:\Users\YourUserName directory where YourUserName is the user name used in your machine such as John.
  • During the installation, install everything but exclude Apache-Tomcat (by unchecking the textbox) that comes with Greenstone3.
  • The installed directory would be C:\Users\YourUserName\Greenstone3.

4. Greenstone3 Configuration

  • Edit the greenstone3.xml in the C:\Users\YourUserName\Greenstone3\resources\web\tomcat by replacing @gsdl3webhome@ with C:\Users\YourUserName\Greenstone3\web for the docBase= variable.
  • Copy and paste the edited greenstone3.xml file into C:\apache-tomecat-6.0.26\conf\Catalina\localhost.
  • Edit build.properties file in the C:\Users\YourUserName\Greenstone3 directory by adding "C:\apache-tomcat-6.0.26" to the tomcat.installed.path variable in the file (This step is required for running Greenstone Librarian Interface (GLI)).

5. Apache Tomcat Configuration

  • Copy and paste all the .dll files in the C:\Users\YourUserName\Greenstone3\lib\jni directory into C:\apache-tomcat-6.0.26\bin directory.
  • Copy and paste all the .jar files in the C:\Users\YourUserName\Greenstone3\lib\jni directory into C:\apache-tomcat-6.0.26\lib directory.
  • (Note: I know that there is a better way to set up these .dll and .jar files for Tomcat server, but I still do not know how to do it yet.)

6. Running Greenstone3

  • Start the Apache Tomcat server
    • Open a command prompt.
    • Issue a command to change directory to the C:\apache-tomcat-6.0.26\bin
    • Issue the following command: startup.bat
  • Open the Greenstone3 web server
    • Open a web browser
    • Type: http://localhost:8080/greenstone3/ in the address bar.
    • The Greenstone 3 welcome page should shown up. On this page, there are four hypertext links.
      • Run the test servlet.
      • Run the default library servlet.
      • Run the ‘standard’ servlet.
      • Run the gateway servlet.
    • The top three links should be working well. The bottom link requires Apache Axis to be installed. (Note: I have not tried it.)
  • Run the Greenstone Librarian Interface (GLI)
    • Open a command prompt.
    • Issue a command to change directory to C:\Users\YourUserName\gli directory.
    • Issue the following command: gli.bat. The GLI should be activated successfully.
  • (Note: I am still not able to run GLI from All Programs > Greenstone-3.04 > Greenstone Librarian Interface (GLI).)

Concluding Remarks

I was spending so much time to get this Greenstone3 up and running on my 64-bit version of Windows 7 machine, a lot of googling and hair pulling. Well, I am glad that I can get it working eventually although some more things need to be done such as running GLI from the All Programs menu of Windows 7. Anyway, I hope that this blog will save you guys time and resources. Have fun and have a good day.

Posted in Uncategorized | 11 Comments

Object-Relational Mapping Using Hibernate (MySQL+Java+Netbeans IDE): Part I

What world are you living in? Relational world or object-oriented world? Maybe you do not know what your answer is. Let ask more specific questions.

Are you modeling your software application using object-oriented approach?, are you using UML (Unified Modeling Language)?, and are you implementing your application using Java, C++ or C#? If you say “yes” to one of these three questions, you are living in an object-oriented world.

Hold on. Are you sure if OO is the only world where you are in? Have you ever used a database for storing data? If you say “yes”, I bet that more than 80% of you guys have used relational database management systems (RDBMS) such as MySQL, MS SQL Server, and Oracle. RDBMSs are systems that provide functionalities and tools (with SQL capabilities) for creating a database, and controling and managing data in the database. The database in this type of systems is a relational database which is developed via the use of Entity-Relationship (ER) or Enhanced-Entity-Relationship (EER) diagram.

So, what do you usually do if you need to store data for your software application? You can still write your application using Java or Object-Oriented approach and use MySQL which is a relational database for storing data. How? Well, you could embed SQL queries within your Java application by using JDBC or Java Database Connector or J/Connector for MySQL. This is also what I usually do.

(Storing data from software applications to be used between sessions is known as providing persistence to data.)

So, you may ask what my point is. Well, the thing is that if you have to embed SQL queries into Java applications, you have to specify SQL queries in advance statically and possibly dynamically or on the fly). To be able to write SQL queries, you certainly must understand the underlying model of the relational database. What if you are not the one who designs the database or you do not know much about the relational model? In addition, an inclusion of SQL queries within your application unavoidably add more complexity to your programs.

Isn’t better if you can just focus on writing your Java programs and the data that you want to store or retrieve can be inserted and retrieved from the relational database automatically without your notice? The answer is “yes”. What you need is a middle man which maps your OO application to relational database (RDB), for example, Java Persistence API, Oracle’s Toplink, Java Data Objects and Hibernate (NHibernate for .NET). These middle men are called Object-Relational Mapping frameworks.

An object-relational mapping framework provides developers transparent persistence to data in their software application development using OO and relational database. In other words, the developers can concentrate on their application development without concerning the underlying relational database (separation of concerns).

OK OK. you may want to ask this question. Why do we need this framework to map OO to relational? Well, the answer is easy. Although OO model looks really similar to relational models or ER/EER models. They are quite different.

In the next part, I will write about their differences, how we can map them, and how to use Hibernate.

Posted in Uncategorized | Leave a comment

Install Flashplayer Plugin for Iceweasel in Debian Lenny 64-bit.

I use the instructions on the web page below, and it works!
http://queleimporta.com/en/finally-adobe-releases-native-64-bit-flash-10-for-linux/

Specifically, I open a terminal, and switch to root. Then, I copy and paste:

wget http://queleimporta.com/downloads/flash10_x64_en.sh && sudo chmod +x flash10_x64_en.sh && sudo sh ./flash10_x64_en.sh

into the terminal. Click the Enter key. That’s it.

Posted in Uncategorized | 1 Comment