Java Creating a Portable Bookmark Library using Java

Creating a Portable Bookmark Library using Java

Java Programming Notes # 2406


Preface

This is Part 1 of a two-part lesson that will teach you how to write a Java program to create and maintain a
portable bookmark (Favorites) library that will follow you from browser to browser, machine to machine, and operating
system to operating system.

Viewing tip

You may find it useful to open another copy of this lesson in a
separate browser window.  That will make it easier for you to
scroll back
and forth among the different listings and figures while you are
reading
about them.

Supplementary material

I recommend that you also study the other lessons in my extensive
collection of online Java tutorials.  You will find those lessons
published
at Gamelan.com
However, as of the date of this writing, Gamelan doesn’t maintain a
consolidated index of my Java tutorial lessons, and sometimes they are
difficult to locate there.  You will find a consolidated index at www.DickBaldwin.com.

General Background Information

A design for an earlier age

The web page bookmark or Favorites system currently in use was
invented at a time when most users had access to a single browser on a single
computer using a single operating system.  That scenario no longer
describes the world in which we live and operate.

Multiple browsers on the same machine

If you are
like me, you routinely use two or three different browsers on the same computer. 
When you create a bookmark to a web page that was accessed using one of those
browsers, it is not readily available to the other browsers without manually
recreating the bookmark in the other browser.

Multiple machines

Many of us also routinely use two or more different computers.  For
example, you may use one computer in your office and a different computer at
home.  I do that, and I also use many different computers in the college
classrooms and labs where I teach.  Bookmarks created in a browser on one computer aren’t readily available on the
other computers.

Multiple operating systems

Some people routinely use two or more different operating systems on the
same, or on different computers.  Once again, bookmarks created under one
operating system probably aren’t readily available under the other operating
system, even if they are on the same computer.

A portable bookmark library

What we need is a bookmark library that will follow us from one browser
to the next on the same machine, from one machine to another, and even from one
operating system to another.  In this lesson, I will show you how to create
and maintain such a portable bookmark library.

What is a bookmark library?

Just for review, a bookmark library is a specialized database containing a list of
names and URLs
for web sites or local files of interest.  In Firefox and Netscape
Navigator, the elements in the database are call bookmarks.  In Internet Explorer
(IE), the elements in the database are called Favorites
They may have different names in browsers with which I am unfamiliar.

(For convenience, I will usually refer to them as bookmarks even with
regard to IE.)

Additional information

In addition to a name and a URL, the bookmark may contain other information
about each bookmark.  However, there isn’t much consistency between Firefox
and IE with regard to this additional information.

For example, the bookmark library in Firefox may maintain the following information
(and possibly more) about each bookmark:

  • Name
  • Location (the URL mentioned above)
  • Keyword
  • Description
  • Date Added
  • Date Last Modified
  • Date Last Visited

IE also maintains other information about each bookmark such as the following:

  • Date created
  • Date last modified
  • Date last accessed

The name and the URL are the most important

I don’t know about you, but the only bookmark information that
I normally care about is the name and the URL.  Occasionally, I may
create and later use a description, but that is extremely rare.

Organization, views, and usage

Different browsers provide different ways for users to organize, view, and
use bookmarks.

The typical usage of a bookmark normally boils down to locating the
bookmark within the bookmark library and selecting that bookmark with the mouse.  The
typical browser response is to open (or attempt to open) the web page or local file
referred to by that bookmark.

Organization into folders

When I first started using browsers many years ago, I began organizing my
bookmarks into folders.  Over the years, I have accumulated more than 5000 bookmarks and my carefully planned
folder system has become totally overwhelmed.

(Many of the 5000+ bookmarks are probably totally obsolete, but I have
never taken the time to go through them and delete those that are obsolete.)

Locate by searching

It seems that these days
(except for about a dozen bookmarks in a folder named Frequently Used)

whenever I want to save a bookmark for later use, I simply dump it into the top
level folder named Bookmarks.  Later when I want to find and use it,
I open the Firefox bookmark option known as Bookmarks Manager where I use
the search feature in an attempt to locate the bookmark. 

Therefore,
except for about a dozen bookmarks that I have organized into a folder named
Frequently Used
, the folder system has effectively become obsolete and my
primary method of finding and using bookmarks is by searching for them.

Searching works reasonably well

There’s nothing wrong with this approach except that the Firefox search
capability isn’t very sophisticated.  A far as I know, it is not possible
to do sophisticated searches using the logical operators AND, OR, and NOT. 
The search system appears to use an implied AND but OR and NOT are not
available.

(One advantage of the Firefox search system, however, is that it has
the ability to do partial word searches.  For example, searching for
the keyword pay will find the bookmark with the name PayPal.com.)

Need a more sophisticated search capability

The fact that I normally end up searching for most bookmarks has recently led me to conclude that I need a bookmark library that will not
only follow me from browser to browser as mentioned earlier, but one which will
also provide a very fast search capability with the ability to use the logical
operators AND, OR, and NOT operators.

A free online bookmarks database

This has led me to conclude that I need access to a (preferably free)
online bookmarks database that can be accessed from any computer, (including computers
in libraries and internet cafes)
, with no requirement for any special
software.

The requirement for no special software means that the required
software must normally be installed on any computer that I am likely to
encounter.  This leads to the conclusion that the online database must be
accessible from the must ubiquitous of all software, the web browser.

So, where can I gain access to a free database that is accessible from any
web browser on just about any computer and which also provides the search
capabilities described above?  One answer is
Google Gmail.  There may also be
other web mail systems that provide similar capabilities as well.

Gmail is a specialized database

When viewed in its most basic sense, Gmail is nothing more or less than a
specialized database that is designed to deal in a highly efficient way with Email
messages.  However, the fact that it is designed to deal with Email
messages doesn’t mean that it can’t also be used to deal with other types of
data, as long as that data can be formatted as Email messages.  One type of
data that can be formatted as Email messages is bookmarks.

Creating a portable bookmark library

Thus, all that is really necessary to create a portable bookmark library is
to:

  • Open a web mail account.
  • Send an Email message to your web mail account for each bookmark that
    you want to save.

The subject of the message should contain the name of the bookmark and the
body of the message should contain the URL.  Obviously, the body could also
contain any other information that you might want to put there including a
description, for example.

Using the portable bookmark library

When time comes to use the bookmark library, all that is necessary is to:

  • Open the web mail account in any compatible browser.
  • Locate the message in the Email archives containing the bookmark of
    interest.
  • Double click on the URL contained in the body of the message.

Obviously if the bookmark library is large, a sophisticated Email search
capability is critical in locating the bookmark of interest.

There is a little more to it than that

If that were all there is to it, there would be no point in me publishing
this lesson.  However, the reality is that the effort required to send an
Email message to yourself for each bookmark that you want to create is much
greater than the effort required to simply click a button in your browser to
cause your browser to create the bookmark and to maintain the bookmark library.

The best of both worlds

What we need, therefore, is the best of both worlds.  We need to
continue to create bookmarks in the browsers that we normally use to do our web
research.  Then we need a simple way to consolidate those bookmarks in a
portable bookmark library of Email messages on a web mail server.

We need a program

Having suggested how you can use your web mail account as a portable bookmark
library, in this lesson I will provide and explain a program that you can:

  • Run initially to populate your web mail bookmark
    library with the hundreds or thousands of bookmarks that you have accumulated
    in the past.
  • Run periodically thereafter to update and consolidate your
    web mail
    bookmark library to incorporate new bookmarks that you have created using one or more
    different browsers on one or more different computers as you do your normal web research.

Firefox, Netscape, and Internet Explorer

The program that I will provide can be used to consolidate Firefox, Netscape,
and Internet Explorer bookmarks into your web mail
bookmark library.  The code that is specific to the different browser
formats is isolated in two methods.  If you use a browser that
creates and maintains its bookmarks in a format that is different from the
above, you should be able to write a new method to handle the bookmark format
for that browser.

My favorite web mail is
Google Gmail

Although I occasionally use three different web mail accounts, the one that I
use most consistently and the one that I know the most about is
Google Gmail.  Therefore, most of the
discussion in this lesson will be slanted toward the use of Gmail for this
purpose.  However, this approach should work equally well with just about
any web mail account provided that it has sufficient capacity, search
capability, and longevity.

Preview

As mentioned earlier, this document is the first part of a two-part lesson. 
This part will explain the overall control code, using method stubs for the
following two methods in place of the methods that actually extract bookmarks
from specific browser bookmark libraries.

  • getFireFoxBookmarks
  • getIEBookmarks

Part 2 will provide and explain those two methods in detail.

Discussion
and Sample Code


This program, named Bookmarks02a, is the same as the program named
Bookmarks02
(that I will provide and explain in Part 2), except that the two
methods listed above have been replaced in this version by stubs. 
You can view the stubs in Listing 22 near the end of the lesson.

Purpose of the program

The purpose of this program is to:

  • Extract bookmarks from one or more Firefox, Netscape, or Internet
    Explorer (IE) browsers within a specified range of bookmark indices.
  • Construct an Email message containing the name and the URL of each
    extracted bookmark.
  • Send the messages to a specified destination Email address.

Email message format

Each Email message is formatted with the name of the bookmark as the Subject
and the URL for the bookmark in the body of the message.  To use this
message as a bookmark later, simply open the message and double-click the
URL.

(Note that the actual sending of the messages may be disabled
for test purposes.  Search for the word disable to find the code that
may be disabled.)

A bookmark code

The Subject may optionally be prepended with a text string provided by the
user and identified as bkMrkCode below.  The string that is prepended to the
subject can be used by the Email client to recognize the message as a bookmark
message.

(If you don’t want anything prepended to the subject, specify this
parameter as an empty string.)

A bookmark history list

The program maintains a list of bookmarks that have previously been sent to
the destination Email address.  The purpose of this list is to prevent the
sending of duplicate bookmark messages to the destination Email address during
successive runs of the program.

The history list is maintained in a text
file that can easily be edited by the user if such editing becomes necessary. 
Several backup copies of the file are automatically generated and maintained by
the program. 

History file is automatically created, backed up,
and maintained

The history file is named BkMrkHistory.txt.  If it doesn’t
already exist, it is automatically created (in a folder specified by the user and
identified below as dataOutPath)
the first time the program is run. 
Once the file exists, it is backed up at the beginning of each successive run of
the program.  Then it is updated during that run.

Prevention of duplicate Email messages

Bookmarks identified in the history list are not sent to the destination Email
address.  This prevents the sending of duplicate bookmark messages during
successive runs of the program.

Once all the bookmarks in the Firefox or
Netscape bookmark file (or the IE Favorites folder) have been sent to the
destination Email address, this program will send only those bookmarks that have
been added or changed since the last time the program was run.

Don’t overwhelm the SMTP server

Because the number of messages that could be sent the first time the program
is run could overwhelm the SMTP server, (causing your ISP to erroneously
conclude that you are distributing SPAM email)
, the program allows the user to specify
the index of the first bookmark to be sent and the maximum number of bookmarks
to be sent during any particular run. 

For example, if values of 0 and 100 are used the first time the program is
run, it can be expected that 100 messages will be sent (assuming that there
are at least 100 non-duplicate bookmarks)
.  These are the messages
required to send bookmarks with indices of 0 through 99.

If values of 0 and 200 are used the second time the program is run, it can be
expected that 100 or more messages will be sent (assuming that there are at
least 200 non-duplicate bookmarks)
.  This will consist of any new
bookmarks in the index range of 0 through 99 plus all of the bookmarks in the
index range of 100 through 199.

Controlled by command-line parameters

This program is controlled using command-line parameters making it very easy
to run periodically using a batch file or a script.

(Once you create the batch file, all that you need to do to run the
program is to start the batch file.  If you put a shortcut to the batch
file on your desktop, you can run the program simply by double-clicking the
shortcut icon.)

The required command-line parameters

The following
values must be provided as command-line parameters.  Although all command-line
parameters are provided as strings, several of the parameters must be convertible to the
int and boolean types shown
below.

  • String destAdr:  Email address to which bookmarks are to be
    sent.
  • String smtpServer:  An SMTP server that can be used to send
    the messages.
  • String bkMrkPath:  Path to the folder containing the Firefox
    or Netscape bookmark file, (which is an HTML file) or containing the IE
    Favorites files (which Microsoft refers to as Internet Shortcut
    files)
    .
  • String bkMrkFile:  Name of the Firefox or Netscape bookmark
    file.  (Provide a dummy file name if you are processing IE
    Favorites.)
  • String dataOutPath:  Path to a folder where output files
    will be stored.  (Make sure this folder is on the path for your regular
    backups because you don’t want to lose these files in case of a disk
    failure.)
  • String bkMrkCode:  Unique code that is prepended to the
    Subject of the Email message to identify the message as a bookmark message.
  • int lowBkMrkLimit:  First bookmark index to process.
  • int numToProc:  Number of bookmarks to process.
  • String browser:  Type of browser: F for Firefox, N for
    Navigator, or I for Internet Explorer.
  • boolean sendMsgs:  Specify true or false.  Messages
    will be sent on true.  Message will not be sent, but bookmark
    statistics will be displayed on false.

Program testing

This program was tested using J2SE 5.0 under WinXP.  J2SE 5.0 or later
is required due to the use of
generics.

The Bookmarks02a class

The class definition and the main method begin in Listing 1.

class Bookmarks02a{

  public static void main(String[] args){
    //Confirm correct number of command-line parameters.
    // If the number is not correct, display a usage msg
    // and terminate the program.
    if(args.length != 10){
      System.out.println("Command-line parameter error");
      System.out.println();
      System.out.println("Usage: java Bookmarks02a");
      System.out.println("followed by:");
      System.out.println("Destination Email address");
      System.out.println("SMTP Server");
      System.out.println("Bookmark path");
      System.out.println("Bookmark file");
      System.out.println("Output data path");
      System.out.println("Bookmark code");
      System.out.println("Low bookmark limit");
      System.out.println("Number bookmarks to process");
      System.out.println("Browser, F, N, or I");
      System.out.println("Permission to send messages");
      System.out.println();
      System.out.println("Terminating Program");
      System.exit(0);      
    }//end if

Listing 1

The code in Listing 1 confirms that the user has entered the correct number
of command-line parameters.  The code prints out some usage information and
terminates the program if the number
of parameters is found to be in error.

Typical command-line parameters

Figure 1 shows a typical set of command-line parameters to extract up to 500
bookmarks from a Firefox browser, format them into Email messages, and send them
to an account at Gmail.


java Bookmarks02a 
[email protected] 
mail.austincc.edu 
"C:/Documents and Settings/Owner/Application Data/
Mozilla/Firefox/Profiles/athy94h2.default/" 
bookmarks.html 
"C:/Documents and Settings/Owner/Application Data/
Mozilla/Firefox/Profiles/athy94h2.default/" 
Bk-Mrk 
0 
500 
F 
true
Figure 1

Because of publication width limitations, each command-line parameter is shown on a
different line in Figure 1.  In addition, each of the two paths shown
in boldface was so long that it was necessary to break it up and display it on
two lines.

You should be able to correlate the command-line parameters
shown in Figure 1 with the list of command-line parameters described
earlier.

Saving the command-line parameters as named
variables

The code in Listing 2 declares named variables and stores the command-line
parameters in those variables.  In a couple of cases, the code also
converts the type of the parameter from String to int or boolean.

    //Email address to which bookmarks are to be sent.
    String destAdr = args[0];
    //An SMTP server that can be used to send the messages.
    String smtpServer = args[1];
    //Path to the folder containing a Firefox bookmark
    // file or containing a multitude of IE .url files.
    String bkMrkPath = args[2];
    //Name of the Firefox bookmark file.  Just use a 
    // dummy name for this parameter when processing IE
    // Favorites
    String bkMrkFile = args[3];
    //Path to the folder where output files will be stored.
    String dataOutPath = args[4];
    //Unique code that identifies a bookmark msg to
    // destAdr.  This code is prepended onto the name of
    // the bookmark and the name is put in the Subject line
    // of the message.
    String bkMrkCode = args[5];
    //Index of first bookmark to process.
    int lowBkMrkLimit = Integer.parseInt(args[6]);
    //Number of bookmarks to process.
    int numToProc = Integer.parseInt(args[7]);
    //Type of browser: F for Firefox, N for Navigator,
    // or I for Internet Explorer.
    String browser = args[8];
    //Send msgs on true.  Just display statistics on false.
    boolean sendMsgs = false;//default value
    //End of command-line parameters
    
    //Convert the last parameter from String to  boolean
    // and overwrite default if appropriate.
    if(args[9].toUpperCase().equals("TRUE")){
      sendMsgs = true;
    }//end if

Listing 2

A new object of type Bookmarks02a

Listing 3 instantiates a new object of the Bookmarks02a class and
stores the reference to that object in a reference variable named thisObj.

    Bookmarks02a thisObj = new Bookmarks02a();

Listing 3

A new bookmark history file

Listing 4 invokes the createHistFile method to create a new empty bookmark
history file if it doesn’t exist.  If it does exist, the createHistFile
method returns quietly having done nothing.

    String bkMrkHistFile = 
                          dataOutPath + "BkMrkHistory.txt";
    thisObj.createHistFile(bkMrkHistFile);

Listing 4

The createHistFile method is so simple that it shouldn’t require an
explanation.  It consists of only one statement that invokes the
createNewFile
method on a new File object.  You can view the
method in Listing 22 near the end of the lesson and read about the
createNewFile
method in the Sun documentation.

Create a TreeSet object

Listing 5 invokes the makeBkMrkHistList method to create a TreeSet
object that encapsulates the bookmarks from the bookmark history file.

(The makeBkMrkHistList method will be discussed in more detail
later.)

This collection is used to store the contents of the history file for
processing.  New bookmarks are added to the object as the corresponding
bookmarks are sent to the destination address, known as destAdr.  The final contents are written
back out into a new history file when the program terminates.

    TreeSet <String>bkMrkHistList = 
                  thisObj.makeBkMrkHistList(bkMrkHistFile);

Listing 5

(Note the use of
the
generics
syntax in Listing 5.)

The bookmark name and URL

Following this, the program gets the name and the URL for each of the
bookmarks.  It encapsulates the name and the URL for each bookmark in an
object of type Bookmark.

The program encapsulates all of the Bookmark objects in an object of
type ArrayList for further processing.

A copy of the bookmark file

As a safety precaution, when processing bookmarks from a Firefox or Navigator browser, the program
invokes a method named copyBkMrkFile to make a copy of the bookmark file. 
(The copy is actually made in Listing 7 later.)  The program processes the copy instead of
processing the original bookmark file in order to reduce the likelihood of
inadvertently corrupting the original bookmark file.  You can view the
copyBkMrkFile
method in Listing 22 near the end of the lesson.  The
code in the method is straightforward, and shouldn’t
require further explanation.

A list of Bookmark objects

Listing 6 instantiates the ArrayList object that is used to
encapsulate all of the Bookmark objects awaiting final
processing.

    ArrayList <Bookmark> theBookmarks = 
                                new ArrayList <Bookmark>();

Listing 6

Populating the ArrayList object

Depending on whether the bookmarks being processed were created by an IE or a
Firefox browser, either the getIEBookmarks method or the
getFireFoxBookmarks
is called to extract the name and the URL from each
bookmark.

As you will see in Part 2 of this lesson, the getIEBookmarks method is
recursive.  As a result, it requires that one of its method parameters
point to the ArrayList object created in Listing 6.  Execution of
the getIEBookmarks method later causes the ArrayList object to be
populated.

The getFireFoxBookmarks method on the other hand is not recursive. 
It creates and populates a new ArrayList object internally.  When it
returns, the ArrayList object that was created in Listing 6 is overwritten
by the new ArrayList object created and populated by the getFireFoxBookmarks
method.

Decide among Firefox, Netscape, and IE

The code in Listing 7 uses one of the command-line parameters to decide
whether to invoke the getFireFoxBookmarks method or the getIEBookmarks
to extract the bookmark information from the browser file(s) and to save the result
in the ArrayList object.

    String tempBkMrkFile = "BkMrkTemp.html";
    if(browser.toUpperCase().equals("F")){
      //Process Firefox bookmarks.
      thisObj.copyBkMrkFile(bkMrkPath,bkMrkFile,
                                dataOutPath,tempBkMrkFile);
      theBookmarks = thisObj.getFireFoxBookmarks(
                                dataOutPath,tempBkMrkFile);
    }else if(browser.toUpperCase().equals("N")){
      //Process Netscape Navigator bookmarks.  Same format
      // as Firefox
      thisObj.copyBkMrkFile(bkMrkPath,bkMrkFile,
                                dataOutPath,tempBkMrkFile);
      theBookmarks = thisObj.getFireFoxBookmarks(
                                dataOutPath,tempBkMrkFile);
    }else if(browser.toUpperCase().equals("I")){
      //Process Inernet Explorer favorites.
      theBookmarks = thisObj.getIEBookmarks(
                                   bkMrkPath,theBookmarks);
    }else{
      System.out.println("Don't recognize browser");
      System.out.println("Terminating program");
      System.exit(0);
    }//end else

Listing 7

Firefox and Netscape bookmark formats are the same

As near as I can determine, the bookmark file format for Firefox and Netscape are the same.  Therefore, the code in Listing 7 invokes the same
method regardless of whether the user specifies Firefox or Netscape Navigator in
the command-line parameter.

(If I learn later that the Netscape format is different from the
Firefox format, I will write a new method to accommodate the Netscape format
and reference the new method in
Listing 7.  This is also the place where you would need to modify the program if
you were to develop a method to extract bookmark information from some
browser other than the three supported by this program as written.)

The code in Listing 7 causes the bookmark information to be extracted from the browser file(s) and
stored as objects of type Bookmark in the ArrayList object that was
instantiated in Listing 6.  That ArrayList object is referred to by theBookmarks.

Very little browser-specific code

At this
point, the bookmark information is ready to be processed, independently of the
specific browser involved.  The only code in this program that is
browser-specific is the code in Listing 7 and the two methods named
getFireFoxBookmarks
and getIEBookmarks.  These two methods extract bookmark
information from the browser file(s).

I will explain these two methods in
detail in Part 2 of this lesson.  For now, suffice it to say that the name
and the URL for each bookmark has been encapsulated in an object of the inner
class Bookmark, and those objects have been encapsulated in the
ArrayList
object referred to by theBookmarks.

Method stubs

The version of the program shown in Listing 22 contains stubs for these two
methods.  These stubs are suitable for testing the overall operation of the program
without a requirement to deal with actual bookmarks.  Each of
the stubs creates dummy information for two different bookmarks and uses that
information to return the required ArrayList
object populated with Bookmark objects.

Process the list of bookmarks

Listing 8 invokes
the processBkMrks method to process the ArrayList object by either

  • Extracting and sending bookmarks as messages to destAdr, or
  • Simply
    extracting and displaying the statistics of the bookmark data. 
    thisObj.processBkMrks(destAdr,
                          smtpServer,
                          bkMrkCode,
                          lowBkMrkLimit,
                          numToProc,
                          sendMsgs,
                          bkMrkHistList,
                          theBookmarks);


Listing 8

Update the history list

The
TreeSet
object created in Listing 5 will be updated to contain an
identification of each bookmark that is sent to destAdr.  At
the beginning of the program, the TreeSet object contains an
identification of all bookmarks that were sent to destAdr during previous
runs of the program.  At the termination of the current run, the TreeSet
object contains an identification of all bookmarks that have been sent to
destAdr
up through the current run.  This procedure is used to avoid sending
duplicate bookmark messages to destAdr during successive runs of the
program.

I will explain the processBkMrks method in detail shortly.

Write a new bookmark history file

Immediately before the program terminates, Listing 9 invokes the writeBkMrkHistFile method to write a new bookmark history file containing the
updated contents of the TreeSet object.

    thisObj.writeBkMrkHistFile(
                              bkMrkHistFile,bkMrkHistList);
  }// end main

Listing 9

I will explain the writeBkMrkHistFile method in detail later in this
lesson.

Listing 9 also signals the end of the main method and the termination
of the program.

The processBkMrks method

Listing 10 shows the beginning of the processBkMrks method.  This
method processes bookmarks previously stored in the ArrayList object
either by the method named getFireFoxBookmarks or by the method named
getIEBookmarks
(One, but not both, of these methods is always
executed once during a run of the program.)

  void processBkMrks(String destAdr,
                     String smtpServer,
                     String bkMrkCode,
                     int lowBkMrkLimit,
                     int numToProc,
                     boolean  sendMsgs,
                     TreeSet <String>bkMrkHistList,
                     ArrayList <Bookmark> theBookmarks){

    int msgCounter = 0;
    String theName = null;
    String theUrl = null;

Listing 10

Send a message or display statistics

If the value of the command-line parameter sendMsgs is true, the processBkMrks method
constructs an Email message for each bookmark and sends it to destAdr
Otherwise the method simply displays statistics regarding the number of messages
that would be sent if the value of sendMsgs were true.

Analyze statistics before you send messages

This latter
option is provided to make it possible for the user to determine in advance how
many messages would be sent for a given set of command-line parameters before
actually sending the messages.

Don’t upset your ISP

This option is useful to avoid overwhelming the SMTP server with an excessive number of
messages in a given run of the program.  For example, when initially
uploading your large library of bookmarks to the web mail server, it may be
necessary for you to meter them out and send only a few hundred bookmarks each
day to avoid upsetting your ISP.  Otherwise, your ISP may erroneously
conclude that you are sending SPAM and disable your ability to send messages.

Bookmarks that are eligible for sending

The processBkMrks method processes the name and the URL for each
bookmark for which data is stored in the ArrayList object referred to by
theBookmarks.

The user
specifies the index of the first bookmark that may be eligible for sending to
destAdr
along with the maximum number of bookmarks that may be eligible.

In the final analysis, a bookmark is deemed eligible for sending to destAdr
only if:

  • It is within the range of bookmarks specified for eligibility and
  • If it is not contained in the bookmark history file.

Duplicates are not eligible for sending

If it is in the
bookmark history file that means that it was previously sent to destAdr
In this case, it is declared ineligible for sending to destAdr in order to avoid
creating duplicate bookmark messages in the archives at destAdr.

A count of eligible messages

The counter that is declared and initialized in Listing 10 counts the number of bookmarks that are eligible for sending to
destAdr according to the criteria given above.

Iterate on the ArrayList object

Listing 11 shows the beginning of a for loop that iterates on the
ArrayList
object.  Code inside the for loop processes each
bookmark contained in the ArrayList object.

    for(int cnt = 0;cnt < theBookmarks.size();cnt++){
      theName = theBookmarks.get(cnt).bkMrkName;
      theUrl = theBookmarks.get(cnt).bkMrkUrl;

Listing 11

Listing 11 also shows the code that extracts the name and the URL for the
bookmark that is being processed during each iteration of the loop.

Determine eligibility for sending

Listing 12 shows the beginning of an if statement that determines is a
particular bookmark is eligible for sending to destAdr according to the
criteria given earlier.

      if((cnt >= lowBkMrkLimit) && 
         (cnt < lowBkMrkLimit + numToProc) &&
         (!bkMrkHistList.contains(theName + theUrl))){
        //This bookmark is eligible for sending to destAdr.
        msgCounter++;
        //Display the names of the eligible bookmark
        // on the command-line screen.
        System.out.println(cnt + " " + theName);

Listing 12

If a the bookmark being processed during the current iteration is deemed to
be eligible for sending to destAdr, a counter is incremented and the name
of the bookmark is displayed on the command-line screen by the code in Listing
12.

Send messages, or simply display statistics

Listing 13 shows the beginning of an if statement that tests the value
of sendMsgs to determine whether to actually send the messages to
destAdr
, or simply to display the statistics regarding eligible bookmarks.

        if(sendMsgs){
          //sendMsgs is true
          String message ="Subject: " + bkMrkCode + ":" + 
              theName + "nn" + " ... " + theUrl + "nn"
                               + "aiNy4^273destAdr274^";

Listing 13

The code in Listing 13 is executed if sendMsgs is true.  This
code constructs the message.

The message format

The code in Listing 13 puts the name of the bookmark in the
Subject line, prepended by the user input string identified by bkMrkCode
concatenated with a colon character.

The first line of the
message body begins with three dots.  Those three dots are followed by the bookmark URL. 
The second line in the message body is left blank.  The third line in the
message body contains a (hopefully) unique code.  The purpose of this code is to make the message
identifiable by a search of the web mail archives without regard to the bookmark
name or the URL.

(The code in the third line is not necessary, but may be handy for use with web
mail archive maintenance.)

Numerous improvements are possible here

There are numerous improvements that you could make with regard to the format
of the message.  For example, if you wanted to make the program
interactive, you could display a GUI at this point and
allow the user to enter a description and one or more keywords to become part of
the body of the Email message.  This could prove to be extremely valuable
later when you are using the web mail search capability to locate a particular
bookmark.

(This is particularly true for Gmail, which doesn’t appear to have a
partial-word search capability.  For example, searching for PayPal on
Gmail wouldn’t find a bookmark named PayPalHome.)

Send the message to destAdr

The code in Listing 14 calls the sendBkMrk method in an attempt to
send the Email message.  The sendBkMrk method returns true if
the message was successfully sent, and returns false otherwise.  The
returned value is saved in the variable named success.

          try{
            boolean success = true;
            //Disable the following statement to avoid
            // sending messages during testing.
            success = sendBkMrk(
                             destAdr,smtpServer,message);

Listing 14

(Sometimes it is useful to disable the actual sending of messages
while testing modifications to the program.  You can disable the
sending of messages by using comment indicators to disable the last
statement in Listing 14.)

If the message was not sent successfully …

Listing 15 throws an exception to display an error message and terminate the
program if the message was not sent successfully.

            if(!success){
              throw new Exception(
                         "Unable to send: " + theName);
            }//end if

Listing 15

(This may be a little extreme.  You may want to modify the code
so that the failure to successfully send a bookmark to destAdr
prevents that particular bookmark from being entered into the history list,
(see Listing 16) but doesn’t terminate the program.  This deserves some
thought.)

Update the book history list

Finally, Listing 16 updates the TreeSet object containing the history list
by adding the bookmark that was just sent to destAdr.  This will
prevent a duplicate of this bookmark message from being sent to destAdr
during some future run of the program.

Note that the information that is used to identify a bookmark in the history
list is the concatenation of the bookmark name and the URL.

            bkMrkHistList.add(theName + theUrl);
          }catch(Exception ex){
            ex.printStackTrace();
            System.exit(0);
          }//end catch
        }//end if
      }//end if
    }//end for loop

Listing 16

Listing 16 also terminates two if statements and the for loop
that began in Listing 11.

Display summary information for the run

Listing 17 displays summary information for the run on the command-line
screen.

    System.out.println(
              "Number eligible bookmarks = " + msgCounter);
    if(sendMsgs){
      //sendMgs is true.
      //All eligigle bookmarks will have been sent.
      System.out.println("Bookmarks sent = " + msgCounter);
    }else{
      //sendMsgs is false.
      System.out.println("Bookmarks sent = " + 0);
    }//end else
    
    System.out.println(
                   "Bookmark range = " + lowBkMrkLimit
                   + " to " + (lowBkMrkLimit + numToProc));
    System.out.println(
        "Total number bookmarks = " + theBookmarks.size());
  }//end processBkMrks

Listing 17

Typical summary information

Figure 2 shows a typical output produced by Figure 17 by running the program
from Listing 22.  The stub method named getFireFoxBookmarks in
Listing 22 generates the two dummy bookmarks shown in Figure 2 and returns them
in the ArrayList object when invoked in Listing 7.  These two dummy
bookmarks were sent to my web mail account when I ran the program that produced
the output shown in Figure 2.

0 Dummy Firefox bookmark 1
1 Dummy Firefox bookmark 2
Number eligible bookmarks = 2
Bookmarks sent = 2
Bookmark range = 0 to 500
Total number bookmarks = 2
Figure 2

The sendBkMrk method

The sendBkMrk method is invoked in Listing 14 to send the bookmark
message to the web mail account.  I have discussed this method, (or methods
very similar to this one)
, in several earlier lessons and won’t repeat that
discussion here.  You can view the sendBkMrk method in its entirety
in Listing 22 near the end of the lesson.

The makeBkMrkHistList method

The makeBkMrkHistList method, which begins in Listing 18, is used to
convert the data in the history text file into a TreeSet object suitable
for processing within the program.  The method also makes a primary backup
and several secondary backups of the history text file before starting to
process the bookmark history data.

  TreeSet <String> makeBkMrkHistList(String bkMrkHistFile){
    TreeSet <String> bkMrkHistList = new TreeSet<String>();

    //Read lines of text from text file and populate the
    // TreeSet object.
    try{
      BufferedReader inData = new BufferedReader(
                            new FileReader(bkMrkHistFile));
      String data; //temp holding area

      while((data = inData.readLine()) != null){
        bkMrkHistList.add(data);
      }//end while loop

      inData.close();//Close input file


Listing 18

Create and populate the TreeSet object

This method reads the history text file and causes each line of text in the
file to become an element in the TreeSet object.

(Note that a TreeSet object contains no duplicates.  Therefore,
if there are duplicates in the text file, they are eliminated at this
point.)

Write the data into a backup text file

After creating the TreeSet object and populating it with the data from
the text file, the method writes the data from the TreeSet object into a
backup file with an extension of bakN, (where the value of N is explained
below)
.

A new backup file with a unique name based on N is created each time the
method is executed (the method is executed once each time the program is run)
Once the number of backup files reaches 5, the method automatically deletes the
oldest file before creating a new backup file.  Thus the method
automatically maintains a sequence of five backup files with extensions bak0
through bak5 with one number missing.

(In case it is necessary to use the backup files to manually restore the
history text file, the age-order of the backup files should be determined by
the modification date on the file and not by the name of the file.)

The code for the primary purpose

The code in the makeBkMrkHistList method that accomplishes the primary
purpose of the method, (which is to populate a TreeSet object from the
history text file)
, is shown in Listing 18.  This code is relatively
straightforward, simply creating a TreeSet object, reading the text file one line at a time, and adding
those lines to the TreeSet object.

Create the backup file

The remainder of the makeBkMrkHistList method, whose purpose is to
create the backup file as described above, is shown in Listing 19.

      //Write a backup file before terminating the method.

      //First determine the name of the next backup file
      // allowed in the directory.
      int N = 0;
      File theFile = null;
      String baseFileName = bkMrkHistFile.
                substring(0,bkMrkHistFile.indexOf(".txt"));
      for(N = 0;N < 6;N++){
        theFile = new File(baseFileName + ".bak" + N);
        if(!(theFile.exists()))break;
      }//end for loop

      //Cause N to rotate from 0 through 5
      if(N == 5){//del file 0 for use next time
        new File(baseFileName + ".bak0").delete();
      }//end if
      else{//delete the next file in sequence
        if(new File(baseFileName + ".bak"
                                      + (N + 1)).exists()){
          new File(
                  baseFileName + ".bak"+ (N + 1)).delete();
        }//end if
      }//end else

      //Now write the output file
      DataOutputStream dataOut = new DataOutputStream(
                            new FileOutputStream(theFile));

      //Use an Iterator object to access the data
      // in the TreeSet object.
      Iterator <String>iter = bkMrkHistList.iterator();

      while(iter.hasNext()){
        data = iter.next();
        dataOut.writeBytes(data + "n");
      }//end while

      dataOut.close();
      
    }catch(Exception e){
      e.printStackTrace();
    }//end catch
    
    return bkMrkHistList;
  }//end makeBkMrkHistList

Listing 19

The code in Listing 19 is also relatively straightforward, so I won’t provide
a detailed explanation.

Return the populated TreeSet object

The last statement in Listing 19 returns a reference to the TreeSet
object that was populated from the history text file in Listing 18.  This
is the object that is used for determination of eligibility by the code in
Listing 12.

The writeBkMrkHistFile method

As bookmarks are turned into Email messages and sent to destAdr, they
are added to the TreeSet object, (shown in Listing 16).  The
writeBkMrkHistFile
method shown in its entirety in Listing 20 writes the
final version of the TreeSet object into the history text file. 
This causes the history text file to contain the identifications of the
bookmarks that were sent to destAdr during the current run, in addition
to the identifications of all the bookmarks that were sent to destAdr
during previous runs.

  void writeBkMrkHistFile(String bkMrkHistFile,
                            TreeSet <String>bkMrkHistList){
    try{
      DataOutputStream dataOut = new DataOutputStream(
                      new FileOutputStream(bkMrkHistFile));

      //Use an iterator to access the data in
      // the TreeSet object.
      Iterator <String>iter = bkMrkHistList.iterator();
      String data;

      while(iter.hasNext()){
        data = iter.next();
        dataOut.writeBytes(data + "n");
      }//end while

      dataOut.close();
    }catch(Exception e){e.printStackTrace();}

  }//end writeBkMrkHistFile

Listing 20

The code in Listing 20 is straightforward, and shouldn’t require further
explanation.

The Bookmark class

Listing 21 shows the definition of an inner class named Bookmark
An object of this class is used to encapsulate the name and the URL for a
bookmark.  I will discuss this class in Part 2 of this lesson in
conjunction with my discussion of the methods named getFireFoxBookmarks
and getIEBookmarks.

  class Bookmark{
    String bkMrkName;
    String bkMrkUrl;
    
    Bookmark(String bkMrkName,String bkMrkUrl){
      this.bkMrkName = bkMrkName;
      this.bkMrkUrl = bkMrkUrl;
    }//end constructor
  }//end inner class Bookmark

Listing 21

Run the Program

I encourage you to copy the code from Listing 22 into your text
editor, compile it, and execute it.  Experiment with it, making
changes, and observing the results of your changes.

For example, make changes to the two method stubs named
getFireFoxBookmarks
and getIEBookmarks, and observe the impact of
those changes on the history text file and also on the inbox at the Email
address to which you send the messages.

Make the program interactive, providing the
capability for the user to enter keywords and a description into the body of the
Email message that will be sent to your web mail account for each bookmark.

If you use a browser other than Firefox, Netscape, or IE, try modifying the
code in Listing 7 to accommodate this new browser.  Then write a method
stub to support those changes.

Remember, you can disable the actual sending of Email messages to avoid cluttering
you inbox while performing these experiments, as explained in Listing 14.

The time interval until Part 2 of this lesson is published would be a good
time for you to clean up your existing bookmark library.  Then it will be
clean when you use the code that I will provide in Part 2 to establish your
portable bookmark library.  Delete the obsolete entries from your library
and make the names more suitable for searching.  If you will be using
Gmail, remember that Gmail doesn’t have a partial-word search capability so use
names like PayPal Home instead of PayPalHome.

Summary

In this lesson, I have taught you part of what you need to know to use Java
to create and maintain a portable bookmark library that will
follow you from browser to browser, machine to machine, and operating system to
operating system.

What’s Next?

This document is Part 1 of a two-part lesson.  Part 2 of this lesson
will be published soon.  Be on the lookout for it.

Complete Program Listing


A complete listing of the program discussed in this lesson is shown in
Listing 22 below.
 

/* File Bookmarks02a.java 
Copyright 2005, R.G.Baldwin
Revised 08/31/05

This program is the same as Bookmarks02, except that the
following two methods have been replaced by stub methods
for test purposes.
  getFireFoxBookmarks
  getIEBookmarks

The purpose of this program is to:

1-Extract bookmarks from one or more FireFox, Netscape, or 
 Internet Explorer (IE) browsers within a specified range 
 of bookmark indices.
2-Construct an Email message containing the name and the
 URL of each extracted bookmark.
3-Send the message to a specified destination Email
 address.
  
(Note that the actual sending of the messages may have been
disabled for test purposes.  Search for the word disable to
find the code that may have been disabled.)

Each Email message is formatted with the name of the 
bookmark as the subject and the URL for the bookmark in the
body of the message.

To use this message as a bookmark later, simply open the
message and double-click on the URL.

The subject may optionally be prepended with a text string 
provided by the user as bkMrkCode below.  The string that 
is prepended to the subject can be used by the Email client
to recognize the message as a bookmark message.  If you 
don't want an actual string prepended to the subject, 
specify this parameter as an empty string, "".

The program also maintains a historical list of bookmarks 
that have previously been sent to the destination Email 
address. The historical list is maintained in a text file 
that can easily be edited by the user if such editing 
becomes necessary.  Several backup copies of the file are 
automatically generated and maintained.

The historical file is named BkMrkHistory.txt.  If it 
doesn't already exist, it is automatically created in a 
folder specified by the user and identified below as
dataOutPath.  Once the file exists, it is backed up at the 
beginning of each successive run of the program.  Then it 
is updated during that run.

Bookmarks in the historical list are not sent to the
destination Email address.  This prevents the sending of 
duplicate bookmark messages during successive runs of the 
program.

Once all the bookmarks in the FireFox or Netscape bookmark 
file (or the IE Favorites folder) have been sent to the 
destination Email address, this program will send only 
those bookmarks that have been added or changed since the 
last time the program was run.

Because the number of messages that could be sent the first
time the program is run could overwhelm the SMTP server,
the program allows the user to specify the index of the
first bookmark to be sent and the maximum number of
bookmarks to be sent during any particular run.  For 
example, if values of 0 and 100 are used the first time the
program is run, it can be expected that 100 messages will 
be sent (assuming that there are at least 100 bookmarks).
These are the messages required to send bookmarks with 
indices of 0 through 99.

If values of 0 and 200 are used the second time the program
is run, it can be expected that 100 or more messages will 
be sent (assuming that there are at least 200 bookmarks).
This will consist of any new bookmarks in the index range 
of 0 through 99 plus all of the bookmarks in the index 
range of 100 through 199.

The following values must be provided as command-line 
parameters.  All command-line parameters are provided as
strings, but must be convertible to the types shown below.

String destAdr: Email address to which bookmarks are to
 be sent.
String smtpServer: An SMTP server that can be used to send
 the messages.
String bkMrkPath: Path to the folder containing the FireFox
 bookmark file, or containing the IE Favorites files.
String bkMrkFile: Name of the FireFox or Netscape bookmark
 file. Provide a dummy name if you are processing IE
 favorites.
String dataOutPath: Path to a folder where output files 
 will be stored.  Make sure that this folder is on the path
 for your regular backups, because you don't want to lose 
 these files in case of a disk failure.
String bkMrkCode: Unique code that is prepended to the
 Subject of the Email message to identify the message as a 
 bookmark message.
int lowBkMrkLimit:  First bookmark index to process.
int numToProc: Number of bookmarks to process.
String browser: Type of browser: F for FireFox, N for
 Navigator, or I for Internet Explorer.
boolean sendMsgs: Specify true or false.  Messages will be
 sent on true.  Message will not be sent, but statistics
 will be displayed on false.

Tested using J2SE 5.0 under WinXP.  J2SE 5.0 or later is
required due to the use of generics.
**********************************************************/

import java.io.*;
import java.nio.channels.*;
import java.util.*;
import sun.net.smtp.SmtpClient;
import java.awt.*;

class Bookmarks02a{

  public static void main(String[] args){
    //Confirm correct number of command-line parameters.
    // If the number is not correct, display a usage msg
    // and terminate the program.
    if(args.length != 10){
      System.out.println("Command-line parameter error");
      System.out.println();
      System.out.println("Usage: java Bookmarks02a");
      System.out.println("followed by:");
      System.out.println("Destination Email address");
      System.out.println("SMTP Server");
      System.out.println("Bookmark path");
      System.out.println("Bookmark file");
      System.out.println("Output data path");
      System.out.println("Bookmark code");
      System.out.println("Low bookmark limit");
      System.out.println("Number bookmarks to process");
      System.out.println("Browser, F, N, or I");
      System.out.println("Permission to send messages");
      System.out.println();
      System.out.println("Terminating Program");
      System.exit(0);      
    }//end if
    
    //The following values are provided as command-line
    // parameters.
    //Email address to which bookmarks are to be sent.
    String destAdr = args[0];
    //An SMTP server that can be used to send the messages.
    String smtpServer = args[1];
    //Path to the folder containing a FireFox bookmark
    // file or containing a multitude of IE .url files.
    String bkMrkPath = args[2];
    //Name of the FireFox bookmark file.  Just use a 
    // dummy name for this parameter when processing IE
    // favorites
    String bkMrkFile = args[3];
    //Path to the folder where output files will be stored.
    String dataOutPath = args[4];
    //Unique code that identifies a bookmark msg to
    // destAdr.  This code is prepended onto the name of
    // the bookmark and the name is put in the Subject line
    // of the message.
    String bkMrkCode = args[5];
    //Index of first bookmark to process.
    int lowBkMrkLimit = Integer.parseInt(args[6]);
    //Number of bookmarks to process.
    int numToProc = Integer.parseInt(args[7]);
    //Type of browser: F for FireFox, N for Navigator,
    // or I for Internet Explorer.
    String browser = args[8];
    //Send msgs on true.  Just display statistics on false.
    boolean sendMsgs = false;//default value
    //End of command-line parameters
    
    //Convert the last parameter from String to  boolean
    // and overwrite default if appropriate.
    if(args[9].toUpperCase().equals("TRUE")){
      sendMsgs = true;
    }//end if

    //Instantiate a new object of this class.    
    Bookmarks02a thisObj = new Bookmarks02a();
    
    //Create a new bookmark history file if it doesn't
    // already exist.  If it does exist, just leave it
    // alone.
    String bkMrkHistFile = 
                          dataOutPath + "BkMrkHistory.txt";
    thisObj.createHistFile(bkMrkHistFile);
    
    //Create a TreeSet object to contain the bookmarks from
    // the bookmark history file.
    //This collection is used to store the contents
    // of the history file for processing.  It gets updated
    // when bookmarks are sent to the destAdr and the final
    // contents are written out into a new history file.
    TreeSet <String>bkMrkHistList = 
                  thisObj.makeBkMrkHistList(bkMrkHistFile);

    //Get the name and the URL for each of the bookmarks.
    // Encapsulate them in an object of type Bookmark.
    // Encapsulate all of the Bookmark objects in an object
    // of type ArrayList.
    
    //When processing bookmarks from a FireFox or Navigator
    // browser,  make a copy of the bookmark file.  Process
    // the copy instead of the original in order to reduce
    // the likelihood of inadvertently  corrupting the
    // original bookmark file.
    
    //The following collection encapsulates all of the
    // bookmarks awaiting final processing.  The
    // getIEBookmarks method requires that a method
    // parameter points to the ArrayList object on input
    // because of its recursive nature.  The
    // getFireFoxBookmarks method is not recursive and it
    // overwrites this object with a new ArrayList object
    // that it creates.
    ArrayList <Bookmark> theBookmarks = 
                                new ArrayList <Bookmark>();
    String tempBkMrkFile = "BkMrkTemp.html";
    if(browser.toUpperCase().equals("F")){
      //Process FireFox bookmarks.
      thisObj.copyBkMrkFile(bkMrkPath,bkMrkFile,
                                dataOutPath,tempBkMrkFile);
      theBookmarks = thisObj.getFireFoxBookmarks(
                                dataOutPath,tempBkMrkFile);
    }else if(browser.toUpperCase().equals("N")){
      //Process Netscape Navigator bookmarks.  Same format
      // as FireFox
      thisObj.copyBkMrkFile(bkMrkPath,bkMrkFile,
                                dataOutPath,tempBkMrkFile);
      theBookmarks = thisObj.getFireFoxBookmarks(
                                dataOutPath,tempBkMrkFile);
    }else if(browser.toUpperCase().equals("I")){
      //Process Inernet Explorer favorites.
      theBookmarks = thisObj.getIEBookmarks(
                                   bkMrkPath,theBookmarks);
    }else{
      System.out.println("Don't recognize browser");
      System.out.println("Terminating program");
      System.exit(0);
    }//end else

    //Process the ArrayList object, either extracting and
    // sending bookmarks as messages to destAdr, or simply
    // extracting and displaying the statistics.  The
    // TreeSet object will be updated to contain an
    // identification of all bookmarks sent to destAdr.
    thisObj.processBkMrks(destAdr,
                          smtpServer,
                          bkMrkCode,
                          lowBkMrkLimit,
                          numToProc,
                          sendMsgs,
                          bkMrkHistList,
                          theBookmarks);
    //Write a new bookmark history file containing the
    // final contents of the TreeSet object.
    thisObj.writeBkMrkHistFile(
                              bkMrkHistFile,bkMrkHistList);
  }// end main
  //-----------------------------------------------------//

  //This method creates a new history file if it doesn't
  // already exist.
  void createHistFile(String bkMrkHistFile){
    try{
      new File(bkMrkHistFile).createNewFile();
    }catch(Exception e){
      e.printStackTrace();
    }//end catch
  }//end createHostFile
  //-----------------------------------------------------//
  
  //This method processes bookmarks previously stored in an
  // ArrayList object.
  //If the value of sendMsgs is true, the method constructs
  // an Email message for each bookmark and sends it to 
  // destAdr. Otherwise the method simply displays
  // statistics regarding the number of messages that would
  // be sent if the value of sendMsgs were true.
  void processBkMrks(String destAdr,
                     String smtpServer,
                     String bkMrkCode,
                     int lowBkMrkLimit,
                     int numToProc,
                     boolean  sendMsgs,
                     TreeSet <String>bkMrkHistList,
                     ArrayList <Bookmark> theBookmarks){
                      
    //Process the name and the URL for the bookmark.  The
    // bookmark is ignored unless it is within the range of
    // bookmarks specified for processing and it is not
    // contained in the bookmark history file.  If it is in
    // the bookmark history file, that means that it was
    // previously sent to the destination address. In this
    // case, it is ignored to avoid creating duplicate
    // bookmark at destAdr.
    
    //The following counter counts the number of bookmarks
    // that are eligible for sending to destAdr according
    // to the criteria given above.
    int msgCounter = 0;
    String theName = null;
    String theUrl = null;
    
    for(int cnt = 0;cnt < theBookmarks.size();cnt++){
      theName = theBookmarks.get(cnt).bkMrkName;
      theUrl = theBookmarks.get(cnt).bkMrkUrl;
      if((cnt >= lowBkMrkLimit) && 
         (cnt < lowBkMrkLimit + numToProc) &&
         (!bkMrkHistList.contains(theName + theUrl))){
        //This bookmark is eligible for sending to destAdr.
        msgCounter++;
        //Display the names of the eligible bookmark
        // on the command-line screen.
        System.out.println(cnt + " " + theName);
                         
        //Decide whether to send the bookmark to destAdr,
        // or to simply display the statistics on eligible
        // bookmarks.
        if(sendMsgs){
          //sendMsgs is true
          //Construct the message.  Put the name of the
          // bookmark in the Subject line, prepended
          // by bkMrkCode + ":".  Put three dots and the
          // URL on the first line of the body of the
          // message.  Leave the third line blank.  Put a
          // unique code on the fourth line to make the
          // message identifiable by a search regardless of
          // the bookmark name and URL.
          String message ="Subject: " + bkMrkCode + ":" + 
              theName + "nn" + " ... " + theUrl + "nn"
                               + "aiNy4DpjnVCgP190KrbCoP";
                          
          try{
            //Call the method to actually send the
            // message.  The method will return false
            // if it is unable to send the message.
            boolean success = true;
            //Disable the following statement to avoid
            // sending messages during testing.
            success = sendBkMrk(
                             destAdr,smtpServer,message);
            if(!success){
              //If unable to send the message.  This will
              // terminate the program and print the
              // error message shown.
              throw new Exception(
                         "Unable to send: " + theName);
            }//end if
            
            //Update the history list to contain the
            // bookmark that was just sent to the
            // destAdr.  Concatenate the name and the
            // URL of the bookmark and put that string
            // in the history list.
            bkMrkHistList.add(theName + theUrl);
          }catch(Exception ex){
            ex.printStackTrace();
            System.exit(0);
          }//end catch
        }//end if
      }//end if
    }//end for loop

    //Display summary information about the run.
    System.out.println(
              "Number eligible bookmarks = " + msgCounter);
    if(sendMsgs){
      //sendMgs is true.
      //All eligigle bookmarks will have been sent.
      System.out.println("Bookmarks sent = " + msgCounter);
    }else{
      //sendMsgs is false.
      System.out.println("Bookmarks sent = " + 0);
    }//end else
    
    System.out.println(
                   "Bookmark range = " + lowBkMrkLimit
                   + " to " + (lowBkMrkLimit + numToProc));
    System.out.println(
        "Total number bookmarks = " + theBookmarks.size());
  }//end processBkMrks
  //-----------------------------------------------------//
  
  //The purpose of this method is to send the message
  // to the destAdr.
  boolean sendBkMrk(String destAdr,
                    String smtpServer,
                    String message){
    try{
      //Pass a string containing the name of the smtp
      // server as a parameter to the  following
      // constructor.
      SmtpClient smtp = new SmtpClient(smtpServer);

      //Pass any valid Email address to the from() method.
      smtp.from(destAdr);

      //Pass the Email address of the destAdr to the
      // to() method.
      smtp.to(destAdr);

      //Get an output stream for the message
      PrintStream msg = smtp.startMessage();
      //Write the message into the output stream.
      msg.println(message);
      //Close the stream, sending the message
      smtp.closeServer();

      return true;//on successful send
    }catch( Exception e ){
      e.printStackTrace();
      System.out.println("while sending Email");
      //Sound an alarm.
      try{
        Thread.currentThread().sleep(500);
        Toolkit.getDefaultToolkit().beep();
        Thread.currentThread().sleep(500);
        Toolkit.getDefaultToolkit().beep();
        Thread.currentThread().sleep(500);
        Toolkit.getDefaultToolkit().beep();
        Thread.currentThread().sleep(500);
      }catch(Exception ex){
        ex.printStackTrace();
      }//end catch
      //Return false to indicate that the msg
      // was not successfully forwarded.
      return false;
    }//end catch

  }//end sendBkMrk
  //-----------------------------------------------------//
  
  //This method reads a text file and causes each line of
  // text in the file to become an element in a TreeSet
  // object.  A TreeSet object contains no duplicates.
  
  //After creating the TreeSet object, the method writes
  // the data from the TreeSet into a backup file named
  // ....bakN, where the value of N is explained below.

  //A new backup file with a unique name based on N is
  // created each time the method is executed. Once the
  // number of backup files reaches 5, the method
  // automatically deletes the oldest file before creating
  // a new backup file.  Thus the method automatically
  // maintains a sequence of five backup files with
  // extensions .bak0 through bak5 with one number missing.
  // The age-order of the files should be determined by
  // the modification date and not by the name of the file.

  TreeSet <String> makeBkMrkHistList(String bkMrkHistFile){
    TreeSet <String> bkMrkHistList = new TreeSet<String>();

    //Read lines of text from text file and populate the
    // TreeSet object.
    try{
      BufferedReader inData = new BufferedReader(
                            new FileReader(bkMrkHistFile));
      String data; //temp holding area

      while((data = inData.readLine()) != null){
        bkMrkHistList.add(data);
      }//end while loop

      inData.close();//Close input file

      //Write a backup file before terminating the method.

      //First determine the name of the next backup file
      // allowed in the directory.
      int N = 0;
      File theFile = null;
      String baseFileName = bkMrkHistFile.
                substring(0,bkMrkHistFile.indexOf(".txt"));
      for(N = 0;N < 6;N++){
        theFile = new File(baseFileName + ".bak" + N);
        if(!(theFile.exists()))break;
      }//end for loop

      //Cause N to rotate from 0 through 5
      if(N == 5){//del file 0 for use next time
        new File(baseFileName + ".bak0").delete();
      }//end if
      else{//delete the next file in sequence
        if(new File(baseFileName + ".bak"
                                      + (N + 1)).exists()){
          new File(
                  baseFileName + ".bak"+ (N + 1)).delete();
        }//end if
      }//end else

      //Now write the output file
      DataOutputStream dataOut = new DataOutputStream(
                            new FileOutputStream(theFile));

      //Use an Iterator object to access the data
      // in the TreeSet object.
      Iterator <String>iter = bkMrkHistList.iterator();

      while(iter.hasNext()){
        data = iter.next();
        dataOut.writeBytes(data + "n");
      }//end while

      dataOut.close();
      
    }catch(Exception e){
      e.printStackTrace();
    }//end catch
    
    return bkMrkHistList;
  }//end makeBkMrkHistList
  //-----------------------------------------------------//
  
  //This method writes the data from a TreeSet object into
  // an output file.
  void writeBkMrkHistFile(String bkMrkHistFile,
                            TreeSet <String>bkMrkHistList){
    try{
      DataOutputStream dataOut = new DataOutputStream(
                      new FileOutputStream(bkMrkHistFile));

      //Use an iterator to access the data in
      // the TreeSet object.
      Iterator <String>iter = bkMrkHistList.iterator();
      String data;

      while(iter.hasNext()){
        data = iter.next();
        dataOut.writeBytes(data + "n");
      }//end while

      dataOut.close();
    }catch(Exception e){e.printStackTrace();}

  }//end writeBkMrkHistFile
  //-----------------------------------------------------//
  
  //This method copies the "official" book mark file into a
  // temporary book mark file to avoid any possibility of
  // corrupting the official book mark file during 
  // processing.
  void copyBkMrkFile(String bkMrkPath,
                     String bkMrkFile,
                     String dataOutPath,
                     String tempBkMrkFile){
    try{
      FileChannel inputChannel = new FileInputStream(
                       bkMrkPath + bkMrkFile).getChannel();
      FileChannel outputChannel = new FileOutputStream(
                 dataOutPath + tempBkMrkFile).getChannel();

      // Copy file contents from input file to output file
      outputChannel.transferFrom(
                     inputChannel, 0, inputChannel.size());
      inputChannel.close();
      outputChannel.close();
    }catch (IOException e){
      e.printStackTrace();
    }//end catch
  }//end copyFile
  //-----------------------------------------------------//
  
  //This is a stub method that creates and returns two
  // dummy bookmarks.  This method is used to test the
  // rest of the program.
  ArrayList <Bookmark> getFireFoxBookmarks(
                  String dataOutPath,String tempBkMrkFile){

    ArrayList <Bookmark> theBookmarks = 
                                new ArrayList <Bookmark>();
    theBookmarks.add(new Bookmark(
                     "Dummy Firefox bookmark 1","theUrl"));
    theBookmarks.add(new Bookmark(
                     "Dummy Firefox bookmark 2","theUrl"));
    return theBookmarks;
  }//end getFireFoxBookmarks
  //-----------------------------------------------------//

  //This is a stub method that creates and returns two
  // dummy bookmarks.  This method is used to test the
  // rest of the program.
  ArrayList <Bookmark> getIEBookmarks(
       String bkMrkPath,ArrayList <Bookmark> theBookmarks){
    theBookmarks.add(new Bookmark(
                     "Dummy IE bookmark 1","theUrl"));
    theBookmarks.add(new Bookmark(
                     "Dummy IE bookmark 2","theUrl"));
    return theBookmarks;
  }//end getIEBookmarks
  //-----------------------------------------------------//
  
  //This is a helper method called by getIEBookmarks.  The
  // purpose of this method is to extract the URL from a
  // Microsoft .url file.
  String getTheUrl(String pathAndFile){
    try{
      BufferedReader inData = new BufferedReader(
                              new FileReader(pathAndFile));
      String data; //temp holding area

      while((data = inData.readLine()) != null){
        if(data.startsWith("URL=")){
          String theUrl = data.substring(4);
          inData.close();//Close input file
          return theUrl;
        }//end if
      }//end while loop
      inData.close();//Close input file
    }catch(Exception e){
      e.printStackTrace();
    }//end catch
    System.out.println("No URL Found");
    return "No URL Found";
  }//end getTheUrl
  //-----------------------------------------------------//
  
  //This is an inner class, the purpose of which is to
  // encapsulate the name and the URL for a bookmark.
  class Bookmark{
    String bkMrkName;
    String bkMrkUrl;
    
    Bookmark(String bkMrkName,String bkMrkUrl){
      this.bkMrkName = bkMrkName;
      this.bkMrkUrl = bkMrkUrl;
    }//end constructor
  }//end inner class Bookmark
  //-----------------------------------------------------//
}//end class Bookmarks02a definition

Listing 22

Copyright 2005, Richard G. Baldwin.  Reproduction in whole or in part in any
form or medium without express written permission from Richard Baldwin is
prohibited.

About the author

Richard Baldwin is a
college professor (at Austin Community College in Austin, TX) and private
consultant whose primary focus is a combination of Java, C#, and XML. In
addition to the many platform and/or language independent benefits of Java and
C# applications, he believes that a combination of Java, C#, and XML will become
the primary driving force in the delivery of structured information on the Web.

Richard has participated in numerous consulting projects and he
frequently provides onsite training at the high-tech companies located in and
around Austin, Texas.  He is the author of Baldwin’s Programming
Tutorials, which have gained a
worldwide following among experienced and aspiring programmers. He has also
published articles in JavaPro magazine.

In addition to his programming expertise, Richard has many years of
practical experience in Digital Signal Processing (DSP).  His first job after he
earned his Bachelor’s degree was doing DSP in the Seismic Research Department of
Texas Instruments.  (TI is still a world leader in DSP.)  In the following
years, he applied his programming and DSP expertise to other interesting areas
including sonar and underwater acoustics.

Richard holds an MSEE degree from Southern Methodist University and has
many years of experience in the application of computer technology to real-world
problems.

[email protected]

Latest Posts

Related Stories