September 2, 2014
Hot Topics:
RSS RSS feed Download our iPhone app

Consolidating Email using Java

  • October 4, 2005
  • By Richard G. Baldwin
  • Send Email »
  • More Articles »

Java Programming Notes # 2400


Preface

Multiple Email accounts?

How many different Email accounts do you have?  Do you check them all regularly?  Do you have an easy way to consolidate them so that you only have to check one Email account in order to read all of your Email?

If you have multiple Email accounts and would like to consolidate all of your Email messages onto a single Email server, then this lesson is for you.

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.

First lesson in a series

This is the first lesson in a series of at least three lessons on the general topic of moving Email messages around among servers and local computers.

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

Multiple Email accounts

If you are like me, you have probably accumulated several different Email accounts over the years.  Unfortunately, the managers of some of my most important Email accounts refuse to forward my Email messages to a single account for consolidation.  As a result, I have had to check several Email accounts several times each day for many years.

Consolidate on local hard drive

Like most people, my operating mode has been to download the mail from several accounts into a single client program on my computer.  I would then process the mail locally.  This processing usually consisted of:

  • Deleting the vast majority of messages which were clearly spam
  • Reading the remainder of the messages
  • Deleting some of the messages after reading them to conserve disk space
  • Filing the rest of the messages away in an elaborate system of Email folders

Over time, my system of Email folders has grown to consume more than 500 MBytes of disk space, so I was always faced with a decision as to whether or not a particular message should be saved.

Let's see, where did I file that message?

This all went pretty well until I needed to find a message later.  Likely as not, when I needed a message later, I couldn't remember where I had filed it.  Searching for the message was clearly out of the question due to the slow speed of the search capability.

What once was a nuisance ...

Until recently, I considered having to check multiple Email accounts and having to search frantically through hundreds of Email folders to find a message to be simply a nuisance.

Gmail to the rescue

However, in May of 2005, mainly out of curiosity, I added Google's Gmail to my list of Email accounts.  Because of its advanced features, large capacity, and lightening-fast search capability, Gmail quickly became my primary Email account.

As of the date of this writing, I consider Gmail to be the best approach to Email that I have ever seen.  Consequently, I have arranged for as many of my other Email accounts as possible to automatically forward my Email messages to Gmail.

(Access to a Gmail account is available by invitation only.  People who already have an account can invite others to open accounts.  As of this writing, I have coupons for fifty invitations.  If you would like to have a Gmail account, send an email message to baldwin@dickbaldwin.com notifying me of that fact and I will submit your email address to get an invitation.  To keep my spam blocker from discarding your message, include Gmail in the subject of the message.)

Some refuse to forward

Unfortunately, as I mentioned earlier, I have a few important accounts where the managers refuse to forward my mail.  As a result, I have been searching for ways to deal with that issue in order to consolidate all of my Email into a single Gmail account.

Consolidation services are available

I looked into the possibility of subscribing to one of the available services that will download all of my Email and consolidate it for a fee.  However, I wasn't too keen on paying a monthly fee for the rest of my life.  I also wasn't too keen on giving the passwords to all of my Email accounts to some company that I know nothing about.

I decided to write my own consolidation program

As a result all of the above, I decided to write my own Email consolidation program and to share it with others who may have the same need.

This program runs locally to download Email messages from one POP3 server and to forward those messages to another Email address.

Although I am using the program to consolidate Email messages from several different POP3 servers onto the Gmail server, it should also be useful for collecting Email messages from any group of POP3 servers and forwarding those messages to any other Email accounts

Preview

An Email forwarding program

In this lesson, I will provide and explain the following two Java classes, which work together as a program to download Email messages from one POP3 server and to forward those messages to another Email account:

  • BigDog04g
  • BigDog04i

Command-line parameters

All of the user-specific information required by the program is provided in the form of the following eight command-line parameters:

  1. pop3Server:  Server from which the messages will be downloaded
  2. userName:  On pop3Server
  3. password:  On pop3Server
  4. destinationAddress:  Email address to which the messages will be forwarded
  5. smtpServer:  An available SMTP server that can be used to forward the messages
  6. workingDir:  Messages are temporarily stored here
  7. archiveDir:  Messages are moved to here from the workingDir for long-term storage if desired (optional)
  8. fwdTag:  String prepended to the subject when the message is forwarded. (Can be an empty string.)

Automation is my long-term goal

I'm aware that parameter input from command-line parameters isn't nearly as sexy as input via a graphical user interface.  However, it is much easier to automate command-line input, and complete automation is my long-term goal.

I have a different batch file set up for each different POP3 server so that all I have to do to move messages from any particular POP3 server to my Gmail account is to double-click on the appropriate batch file.

There are two simple GUIs

There are two very simple GUIs involved in this version of the program (although I expect them to disappear in a future version).  When the program first starts running, the GUI shown in Figure 1 appears on the screen.  The program pauses until the user clicks the button labeled Download Msgs.  Then the program proceeds to download the messages.


Figure 1

Forwarding the messages

When all of the messages have been downloaded, the GUI shown in Figure 2 appears on the screen.  The program pauses until the user clicks the button labeled Forward Msgs before proceeding to forward the messages to the destination Email address.


Figure 2

These first two button clicks will almost certainly be eliminated from a future version of the program.

Deleting messages from the POP3 server

Once all of the messages have been forwarded to the destination Email address, the program pauses once more and waits for the user to click the button labeled Delete Msg On Server before actually deleting the messages from the POP3 server.  This gives the user an opportunity to take a look at the messages on the destination server before deleting them from the POP3 server.

An overabundance of caution

You might say that the use of these three stages of user interaction were the result of an overabundance of caution on my part.  I wanted to have some degree of control over the program when I first started running it in order to satisfy myself that messages weren't being lost in the process.

I will also probably eliminate all of this user interaction once I become completely confident that all messages are being reliably transferred from one Email server to the other.

Running automatically on a scheduled basis

At that point in time, there will be no required user interaction other than to start the program running.  Once I reach that point, it shouldn't be too difficult to set the batch file up in a task scheduler to cause the program to run automatically without user interaction on a scheduled basis, such as once per hour for example.  That is my ultimate goal.

Status and progress information

Figure 1 and Figure 2 also show a TextArea in the GUI.  Some status and progress information is displayed in these two areas, as well as in the command-line window while the program is running.

Operation is extremely easy

With this version of the program, all the user has to do to cause the program to move the available Email messages from a POP3 server to another Email account is to double click on a batch file, and then click on three GUI buttons in succession.  Program operation could hardly be simpler.

A batch-file command

A typical batch file command is shown in Figure 3.

(Note that it was necessary for me to manually enter line breaks into Figure 3 to force the command to fit in this narrow publication format.)

java BigDog04g pop.austincc.edu user password 
myAcct@gmail.com smtp-server.austin.rr.com 
./temp/ c:/z-MailArchives/ "ACC-"
Figure 3

You should be able to compare the individual command-line parameter in Figure 3 with the description of the parameters discussed earlier.

Discussion and Sample Code

This program consists of the following two classes:
  • BigDog04g
  • BigDog04i

The class named BigDog04g is a Java application and serves as the driver for the entire program.

Overall control

BigDog04g downloads all of the available messages from a POP3 server and writes them as individual files in a local working directory.  Then it instantiates an object of the class BigDog04i, which forwards the messages to a specified Email address.

Deleting messages and moving message files

Messages are not deleted from the POP3 server until they have been forwarded to the specified Email address and the user has directed that they be deleted.  When the messages are deleted from the POP3 server, the message files are also removed from the working directory.

By enabling and disabling blocks of code prior to compilation, a user can customize the program to cause the message files to be moved from the working directory into an archive directory or to simply be deleted from the working directory without saving them in an archive directory.

Command-line parameters

The following information is provided by the user in the form of command-line parameters:

  1. pop3Server: server from which the messages will be downloaded
  2. userName: on pop3Server
  3. password: on pop3Server
  4. destinationAddress: Email address to which the messages are to be forwarded
  5. smtpServer: an available SMTP server that can be used to forward the messages
  6. workingDir: messages are temporarily stored here
  7. archiveDir: messages are moved to here from the workingDir for long-term storage if desired (optional)
  8. fwdTag: String prepended to the subject when the message is forwarded

Directories must already exist

Note that both of the directories listed above must already exist when the program is run for the first time.

If message files are not being saved in an archive directory, the seventh parameter is a dummy parameter, which is essentially ignored by the program.  Even though it is ignored, it must be provided as a command-line parameter to cause the number of parameters to be correct.

Tagging the forwarded messages

The eighth parameter is a string that is prepended onto the beginning of the Subject line in the message before the message is forwarded.  For example, it can be used to indicate the originating Email account.  If you don't want to tag the Subject of the forwarded message, just enter an empty string, "", as the eighth command-line parameter.

POP3 technical information

For technical information on POP3, see RFC 1725at http://www.cse.ohio-state.edu/cgi-bin/rfc/rfc1725.html

A POP3 Command Summary is provided in the program comments in Listing 45 near the end of the lesson.

Program testing

The program was successfully tested using JDK 1.5.0_01 running under WinXP.  JDK 1.5 or later is required because of the use of generics in the program.

Will discuss in fragments

I will break both classes down and discuss them in fragments.  Complete listings of both classes are provided in Listing 45 and Listing 46 near the end of the lesson.

BigDog04g is a Frame

The class definition for BigDog04g begins in Listing 1.  As you can see, the class extends Frame, so an object of the class is a Frame.  The Frame that constitutes BigDog04g was shown earlier in Figure 1.

class BigDog04g extends Frame{
  static String workingDir;
  int numberMsgs = 0;
  int msgNumber;
  String uidl = "";//unique msg ID
  BufferedReader inputStream;
  PrintWriter outputStream;
  Socket socket;
  String pathFileName;
  static String[] params;

Listing 1

The code in Listing 1 simply declares some necessary variables.

The main method

BigDog04g is a Java application and therefore requires a main method.  The main method begins in Listing 2.

As mentioned earlier, certain critical information is provided to the program by way of eight command-line parameters.  The code in Listing 2 tests to confirm that the user has entered eight command-line parameters.  If not, usage information is displayed and the program terminates at that point.

  public static void main(String[] args){
    if(args.length != 8){
      System.out.println(
                   "Wrong number of parameters");
      System.out.println("Usage: java BigDog04g "
         + "npop3Server nuserName npassword "
         + "ndestinationAddress nsmtpServer"
         + "nworkingDir narchiveDir nfwdTag");
      System.out.println("If not saving msg "
        + "files in archive, seventh parameter "
        + "is a dummy parameter.");
      System.out.println("Terminating program.");
      System.exit(0);
    }//end if

Listing 2

Save command-line parameter values

Assuming that the number of command-line parameters is correct, the program saves some of them and uses others.

Listing 3 saves a reference to the args array so that it will be available later.

Listing 3 also identifies and saves the name of the working directory where the message files will be temporarily stored awaiting forwarding to the destination Email address.

    params = args;
    workingDir = args[5];

Listing 3

(Once again, note that for proper operation, the working directory and the archive directory must both exist when the program is run.  The program is not designed to create those directories if they don't already exist.)

Instantiate a BigDog04g object

Finally, as shown in Listing 4, the main method uses the first three command-line parameters to instantiate an object of the BigDog04g class.

    new BigDog04g(args[0],args[1],args[2]);
  }//end main

Listing 4

Once constructed, this object will download and save the messages on the specified POP3 Email server as individual files in the local working directory.

The BigDog04g object will, in turn, instantiate an object of the BigDog04i class, which will forward the messages in the working directory to the destination Email address.

The BigDog04g constructor

The BigDog04 constructor begins in Listing 5.  The first task of the constructor is to get a connection to the POP3 Email server using the three incoming parameters.

  BigDog04g(String pop3Server,String userName,
                                String password){
    int port = 110; //pop3 mail port
    try{
      socket = new Socket(pop3Server,port);

Listing 5

Listing 5 begins by getting a Socket connection to the specified POP3 server on port 110, which is the standard POP3 Email port.

Get input and output streams

Next, as shown in Listing 6, the constructor gets input and output streams on the Socket, which can be used to communicate with the POP3 server.  This should be old stuff to you by now.  If not, see my earlier lessons 550 through 568 on Network Programming.

      //Get an input stream from the socket
      inputStream = new BufferedReader(
                     new InputStreamReader(
                       socket.getInputStream()));
      //Get  an output stream to the socket.
      // Note that this stream will autoflush.
      outputStream = new PrintWriter(
               new OutputStreamWriter(
                 socket.getOutputStream()),true);

Listing 6

The validateOneLine method

At this point, I am going to set the discussion of the constructor aside and explain a utility method named validateOneLine.

(The BigDog04g and BigDog04i classes both contain copies of this utility method.)

This method is shown in its entirety in Listing 7.

  private String validateOneLine(){
    try{
      String response = inputStream.readLine();
      if(response.startsWith("+OK")){
        return response;
      }else{
        System.out.println(response);
        //Terminate the session.
        outputStream.println("QUIT");
        socket.close();
        System.out.println(
                       "Premature QUIT on -ERR");
        System.exit(0);
      }//end else
    }catch(IOException ex){
      ex.printStackTrace();
    }//end catch
    //The following return statement is required
    // to satisfy the compiler, but it can never
    // be reached.
    return "Make compiler happy";
  }//end validateOneLine()

Listing 7

Purpose of the validateOneLine method

The purpose of the validateOneLine method is to get and to validate a one-line message received from the POP3 server.  That message is the POP3 server's response to a command that was sent to the server by the program.

The response may begin with the substring +OK or it may begin with the substring -ERR.

(The difference between the two should be fairly obvious.  There may be other possibilities as well, but if so this program doesn't specifically take them into account.)

Validating the response

If the response of the POP3 server to the previous command was +OK, the validateOneLine method considers the command to have been successful and returns the string.

If the response was not +OK, it is assumed that the response was -ERR (or possibly something else indicating that the previous command was not successful).  In that case, the method displays the string received from the POP3 server and terminates the session with the server.

Code is straightforward

The actual code in Listing 7 is fairly straightforward and shouldn't require further explanation.

(Note that the code in Listing 7 uses the inputStream from Listing 6 to read a single line of text that is sent by the server.)

Display connection message

Now, getting back to the constructor for the BigDog04g class, the constructor has just attempted to get a Socket connection to the POP3 server, (along with input and output streams on that socket) by which the program can communicate with the server.

The code in Listing 8 invokes the validateOneLine method to get and to display the initial response sent by the POP3 server to the connection request.

      String connectMsg = validateOneLine();
      System.out.println(
        "Connected to pop3Server " + connectMsg);

Listing 8

Typical connection response

Figure 4 shows a typical response to a connection request that might be received from a POP3 server.

(Note that I manually entered line breaks into figure 4 to force it to fit into this narrow publication format.)

Connected to pop3Server
+OK POP3 mailbox.austincc.edu 
v2001.78 server ready
Figure 4

Establish the user on the POP3 server

The next task is to establish a valid user on the POP3 server. 

The communication process between the program and the POP3 server is now in the AUTHORIZATION state.  The next step is to send the username and password to the POP3 server.

Commands are sent in uppercase plain text to the POP3 server.  Some commands require an argument following the command, as is the case with the USER command.

Send a USER command

The code in Listing 9 uses the outputStream object obtained in Listing 6 to send the USER command along with its argument to the POP3 server.

Then the code in Listing 9 invokes the validateOneLine method to get, validate, and display the response from the POP3 server.

      outputStream.println("USER " + userName);
      String userResponse = validateOneLine();
      System.out.println("USER " + userResponse);

Listing 9

Typical response to a USER command

Figure 5 shows a typical response to a USER command, provided of course that the user name that is provided as an argument to the command is a valid user.

USER +OK User name accepted, password please
Figure 5

As you can see from Figure 5, the next required step is to provide the password that matches the user name.

Send the PASS command

The code in Listing 10 sends the PASS command along with the password to the POP3 server.

Then Listing 10 invokes the validateOneLine method to get, validate, and display the response to that command.

      outputStream.println("PASS " + password);
      System.out.println(
                    "PASS " + validateOneLine());
                    
      System.out.println("Downloading Messages");
    }catch(Exception e){e.printStackTrace();}

Listing 10

Typical response to the PASS command

Figure 6 shows a typical response to the PASS command assuming that the password that is provided is a valid password.

PASS +OK Mailbox open, 1 messages
Figure 6

As you can see, the response in Figure 6 not only opened the mailbox for business, it also notified the program of the number of available messages.  However, providing the number of available messages at this point is peculiar to this particular POP3 server and is not consistent from one server to the next.

(In fact, much of the text in the server response following the +OK characters will vary from one server to the next for different commands.)

Keeping track of what is going on

The last print statement in Listing 10 simply helps the user keep track of things as they occur.

Register a WindowListener object

The code in Listing 11 registers an anonymous WindowListener on the Frame to support the close button in the upper-right corner.

    this.addWindowListener(
      new WindowAdapter(){
        public void windowClosing(WindowEvent e){
          System.exit(0);
        }//end windowClosing
      }//end WindowAdapter()
    );//end addWindowListener

Listing 11

If you are unfamiliar with the code in Listing 11, see lessons 1632 through 1642 in my earlier publications.

Instantiate GUI objects

Listing 12 instantiates the Button and TextArea objects shown in the GUI in Figure 1.

    final Button downLoadButton =
                     new Button("Download Msgs");
    final TextArea textArea = new TextArea(5,30);

Listing 12

The references to these objects must be final because they are accessed from within an anonymous class definition.  This is also explained in my earlier publications referred to above.

Register an ActionListener object

In this version of the program, message downloading begins when the user clicks the button shown in Figure 1.  The actual downloading of the messages is controlled by code contained in the actionPerformed method of an ActionListener object registered on that button.

The code in Listing 13 is the beginning of the code required to define, instantiate, and register an anonymous ActionListener object on the Button shown in Figure 1.

The purpose of this object is to download all of the available messages on the POP3 server when the user clicks the button.

    downLoadButton.addActionListener(
      new ActionListener(){
        public void actionPerformed(
                                  ActionEvent e){

Listing 13

Listing 13 shows the beginning of the anonymous class definition, as well as the beginning of the actionPerformed method, which is executed when the user clicks the button to download the messages.

Get number of messages

The communication process between the program and the POP3 server is now in the TRANSACTION state.  While in this state, the ActionListener object will get and save the messages as individual files in the working directory.

The first step is to get the number of available messages on the server in a format that is useful.  The code in Listing 14 begins by sending a STAT command to the POP3 server to get a string that includes the number of messages.  A typical response to the STAT command is shown below, indicating that there are five messages available:

+OK 5 62677

          try{
            outputStream.println("STAT");
            String stat = validateOneLine();
            //Get the number of messages as
            // a String.
            String numberMsgsStr =
                     stat.substring(
                          4,stat.indexOf(" ",5));
            //Convert the String to an int.
            numberMsgs = Integer.parseInt(
                                  numberMsgsStr);

Listing 14

Parse the string

Then the code in Listing 14 parses the string to extract a substring that specifies the number of messages as a String.

Then the code in Listing 14 invokes the parseInt method of the Integer class to convert that string to a numeric value of type int.

Download the messages

Having gotten permission to download the messages, and having determined how many messages are available for downloading, the next step is to actually download the messages and to save each message in a separate file in the working directory.

This process is accomplished using nested while loops.  The outer loop iterates on messages.  The inner loop iterates on lines within an individual message.

I will explain this process from the outside in.  In other words, I will explain the details of the outer loop while ignoring the details of the inner loop.  Then I will go back and explain the details of the inner loop.

The outer loop

The outer loop begins in Listing 15.  This loop iterates on message numbers in terms of the POP3 server's definition of message numbers.

(Note that message numbers begin with 1 instead of 0, so the loop is constructed accordingly.)

            msgNumber = 1;
      
            while(msgNumber <= numberMsgs){
              //Process the next message.
              outputStream.println(
                            "UIDL " + msgNumber);
              uidl = validateOneLine();

Listing 15

A unique identifier

Each message on the POP3 server is identified by a unique identifier in addition to the message number, (known as uidl in this program).  The uidl is used by this program to construct a local file name for the message.

(Although the POP3 server guarantees that no two messages on that server will have the same uidl, there is no guarantee that two messages on different POP3 servers won't have the same uidl.  Therefore, it is not wise to combine message files from two different POP3 servers in the same directory.  While not very likely, it is possible that this would result in two identical file names.  This program was not designed to protect against such conflicts.)

The code in Listing 15 gets, validates, and saves the uidl for the next message.  It sends a UIDL command along with the message number to the server to cause the server to send back the uidl.  An example of the server's response to this command is shown below:

+OK 12 40d1c47e00012b11
More than just the unique identifier

As you can see, the server's response, which is saved as uidl by this program, includes more than just the unique identifier.  The actual unique identifier is the sixteen-character hexadecimal value at the right end of the string.

The response also contains the message number and the +OK indicating that it is a good response.

Open an output file

The code in Listing 16 uses the contents of uidl to open an output file into which the message will be stored.  This results in a series of unique file names similar to that shown above.

              pathFileName = workingDir + uidl;
              DataOutputStream dataOut =
                          new DataOutputStream(
                            new FileOutputStream(
                                  pathFileName));

Listing 16

Retrieve the message

Next, the code in Listing 17 sends a RETR command to the POP3 server to retrieve the message identified by msgNumber from the server.

              outputStream.println(
                            "RETR " + msgNumber);
              //Validate the response.
              String retrResponse =
                               validateOneLine();
//Inner loop removed for brevity.

Listing 17

Response to the RETR command

In response, the server sends a string similar to that shown below back to the program.

+OK 6052 octets

As you can see, this response contains the size of the message.  However, this program doesn't make use of that information.

(Although I have seen this size information for two different servers, I don't know if this format is consistent from one server to the next.)

This program simply invokes the validateOneLine method to confirm that the response begins with +OK.  Then it executes the inner loop, (which was deleted for brevity from Listing 17), to get and save the message in a local file in the working directory.

(As mentioned earlier, I will explain the code in the inner loop shortly.)

When the inner loop terminates ...

Once the inner loop terminates, the three statements in Listing 18 are executed for the purpose of:

  • Closing the file.  At this point, the message is stored in a local file in the working directory with a file name that is based on the unique identifier provided by the POP3 server.
  • Displaying a message number on the command-line screen to indicate the state of progress to the user.
  • Incrementing the message number in preparation for executing the next iteration of the outer loop to download the next available message.

              dataOut.close();
              System.out.print(msgNumber + " ");
              msgNumber++;
            }//end while(msgNumber <= numberMsgs)

Listing 18

Listing 18 also signals the end of the outer loop.

Retrieve the contents of each message

Listing 19 shows the inner loop, which is used to retrieve the contents of each message.

              String msgLine =
                          inputStream.readLine();
              while(!(msgLine.equals("."))){
                dataOut.writeBytes(
                                 msgLine + "n");
                                 
                msgLine = inputStream.readLine();
              }//end while(!(msgLine.equals(".")

Listing 19

Each message consists of at least two lines of text.  The last line of text consists of a single period character as the first character in the line.  This special line format signals the end of the message.

Typical message text

In case you are interested, Figure 7 consists of the first four lines of text from a typical message. 

(Note, however, that it was necessary for me to manually enter several line breaks into Figure 7 to force it to fit into this narrow publication format.  As a result, Figure 7 shows more than four lines of text.)


Return-Path: <abc@def.com>
Received: from mailgateway.austincc.edu 
  (root@ACCess1.austincc.edu [198.213.3.20] 
  (may be forged))
 by mailbox.austincc.edu (8.12.3/8.12.3/
 Debian-6.6) with ESMTP id j7^261Gmail262^018934;
 Sun, 14 Aug 2005 11:32:07 -0500
Figure 7

Begin with a priming read

The code in Listing 19 begins with a priming read to read the first line of text.  Then it enters a while loop to process that line, and to read and process each succeeding line, until the terminating line containing the single period is received.

Processing consists of writing each line of text into the output file, inserting a newline character at the end of each line in the output file.

Disable the download button

When the outer loop terminates, all of the available messages have been downloaded.  The code in Listing 20 disables the button labeled Download Msgs in Figure 1 to prevent the user from attempting to download the same messages a second time.

(In retrospect, I probably should have placed this code near the beginning of the actionPerformed method to prevent the user from fiddling with the button in the event of a long pause in the download process.)

            downLoadButton.setEnabled(false);
            textArea.append(
                        "Messages downloaded");

Listing 20

Listing 20 also displays a message in the GUI of Figure 1 to notify the user that the messages have been downloaded.

Terminate the POP3 session

The code in Listing 21

  • Displays a disconnect message on the command-line screen to inform the user as to what is happening.
  • Sends a QUIT command to the POP3 server to terminate the session.
  • Gets, validates, and displays the response from the POP3 server (see Figure 8).
  • Closes the socket connection to the POP3 server.

            System.out.println(
              "nDisconnect from pop3Server.");
              
            outputStream.println("QUIT");
            String quitResponse =
                             validateOneLine();
            System.out.println(
                       "QUIT " + quitResponse);
            System.out.println();//blank line
            
            socket.close();

Listing 21

Typical termination response

The text in Figure 8 is typical of that which is produced by the code in Listing 21.  Note that the actual text in the response will vary from one POP3 server to the next, however.

Disconnect from pop3Server.
QUIT +OK Sayonara
Figure 8

Sound an audible alert

The code in Listing 22 sounds an audible alert to get the attention of the user, because in this non-automated version of the program, it is time for the user to click another button.  (As discussed earlier, this requirement will be eliminated in a future version of the program.)

            Toolkit.getDefaultToolkit().beep();
            try{
              Thread.currentThread().sleep(500);
            }catch(Exception ex){
              System.out.println(ex);
            }//end catch
            Toolkit.getDefaultToolkit().beep();

Listing 22

Instantiate a BigDog04i object

At this point, all of the messages have been downloaded from the POP3 server and have been saved as individual files in the working directory.  It is time to forward those messages to the destination Email address.  The forwarding task is handled by an object of the class named BigDog04i.

The code in Listing 23 instantiates an object of the BigDog04i class to cause the message forwarding process to take place.

            new BigDog04i(
                params[0],//pop3Server
                params[1],//userName
                params[2],//password
                params[3],//destinationAddress
                params[4],//smtpServer
                params[5],//workingDir
                params[6],//archiveDir
                params[7]);//fwdTag
          }//end try
          catch(Exception ex){
            ex.printStackTrace();
          }//end catch
        }//end actionPerformed
      }//end ActionListener
    );//end addActionListener

Listing 23

Cleanup code

Listing 23 also signals the end of the anonymous definition and instantiation of the ActionListener class and its actionPerformed method.

Once again, if you are unfamiliar with the cleanup code in Listing 23, see lessons 1632 through 1642 in my earlier publications.

Configure the GUI

The remaining code in the constructor shown in Listing 24 configures the GUI shown in Figure 1 by placing the various components onto the Frame, setting the size, and making the whole thing visible.

    add(downLoadButton);
    add(textArea);
    textArea.setText("");
    setLayout(new FlowLayout());
    setTitle("Copyright 2005, R.G.Baldwin");
    setBounds(0,0,250,200);
    //Make the GUI visible.
    setVisible(true);
  }//end constructor
  //===========================================//
}//end class BigDog04g

Listing 24

Listing 24 also signals the end of the BigDog04g class.

The bottom line regarding the BigDog04g object

Once the BigDog04g GUI becomes visible on the screen, the user can cause the program to connect to the specified POP3 server and to download the available messages by clicking the button labeled Download Msgs shown in Figure 1.

The BigDog04i class

Now that you understand how the program downloads the messages from the POP3 server, it is time to learn how it forwards those messages to the destination Email address.

An object of the BigDog04i class is instantiated by the code in Listing 23 to tag and forward a set of message files that were written into the working directory by the BigDog04g object.

Tag and forward the messages

This BigDog04i object tags the messages with a tag defined by the user as a command-line parameter to the main method of the class named BigDog04g.  Then the object forwards the messages to a destination Email account that is specified as a command-line parameter.  It uses an SMTP server that is also specified as a command-line parameter.

The DELE command

This object uses the DELE command to delete messages from the POP3 server from which the messages were originally downloaded.

(Deletion of the messages from the server can be temporarily disabled for test purposes by disabling a block of code before compiling the program.  Search for the word disable in the source code to identify the code that needs to be disabled for this purpose.)

Program testing

The BigDog04i class was tested using JDK 1.5.0_01 running under WinXP.  JDK version 1.5 or later is required due to the use of generics in the program.

The BigDog04i class

The class definition begins in Listing 25.  As you can see, the class extends Frame.  An object of this class is shown in Figure 2.

class BigDog04i extends Frame{
  //User-specific information is stored here.
  // This information is provided in the form of
  // constructor parameters.
  //ID of the destination Email account.
  String destinationAddress;
  //An smtp server through which the user is
  // authorized to send Email messages.
  String smtpServer;
  //Local folder where message files are stored
  // awaiting processing.
  final String workingDir;
  //Local folder for archiving message files.
  final String archiveDir;
  //Tag that is prepended to the Subject line of
  // the message before forwarding.
  String fwdTag;
  //Following are working variables used by the
  // program for various purposes.
  BufferedReader inputStream;
  PrintWriter outputStream;
  Socket socket;
  String pathFileName;
  Vector <String> msgToDelete = 
                            new Vector<String>();
  Button forwardButton = 
                      new Button("Forward Msgs");
  Button deleteButton = new Button(
                         "Delete Msg On Server");
  TextArea textArea = new TextArea(5,30);
  String uidl;
  boolean okToDelete = false;
  int msgNumber = 0;

Listing 25

The code in Listing 25 declares a large number of variables, initializing some of them.  This code should be self-explanatory and shouldn't require further explanation.

The constructor for BigDog04i

The constructor begins in Listing 26.

  BigDog04i(final String pop3Server,
            final String userName,
            final String password,
            String destinationAddress,
            String smtpServer,
            final String workingDir,
            final String archiveDir,
            String fwdTag){
    this.destinationAddress = destinationAddress;
    this.smtpServer = smtpServer;
    this.workingDir = workingDir;
    this.archiveDir = archiveDir;
    this.fwdTag = fwdTag;
    
    System.out.println("Forwarding messages:");

Listing 26

Once again, the code in Listing 26 should be self-explanatory and shouldn't require further explanation.

Register a WindowListener object

Listing 27 registers a WindowListener object to service the close button on the Frame.

    this.addWindowListener(
      new WindowAdapter(){
        public void windowClosing(WindowEvent e){
          System.exit(0);
        }//end windowClosing
      }//end WindowAdapter()
    );//end addWindowListener

Listing 27

Register an ActionListener

The forwarding of messages begins when the user clicks the button labeled Forward Msgs in Figure 2.  Listing 28 shows the beginning of the code that is used to anonymously define, instantiate, and register an ActionListener object on that button.

    forwardButton.addActionListener(
      new ActionListener(){
        public void actionPerformed(
                                  ActionEvent e){
          forwardButton.setEnabled(false);

Listing 28

Clicking this button causes the messages stored as files in the working directory to be forwarded to the destination Email address. 

Don't fiddle with that button

The code in the actionPerformed method begins by disabling the button to prevent the user from attempting to forward the same set of messages twice.

(See the retrospective comment that I made regarding the ActionListener object that is registered on the button in Figure 1.  This is more important here, because it usually take much longer to forward the messages than it does to download them.  During the forwarding process, there is often plenty of time for the user to fiddle with the button.)

Get a directory listing

In order to be able to forward the messages, the object must have a list of the message files that are to be forwarded.

The code in Listing 29 gets a directory listing of the message files in the working directory.  This code creates a directory listing containing only those files with names that begin with +OK.  This is an anonymous implementation of a class that implements FilenameFilter.

          String[] dirList = 
                       new File(workingDir).list(
            new FilenameFilter(){
              public boolean accept(
                           File dir,String name){
                if(!(new File(dir,name).
                  isFile())) return false;
                return name.startsWith("+OK");
              }//end accept
            }//end FilenameFilter
          );//end list

Listing 29

Process all of the message files

Listing 30 shows the beginning of a for loop that is used to process all of the message files in the working directory, one message at a time.

          for(int msgCounter = 0;
                     msgCounter < dirList.length;
                                   msgCounter++){
            String fileName =
                             dirList[msgCounter];
            pathFileName = workingDir + fileName;

Listing 30

The code in listing 30 constructs a pathFileName string as the concatenation of the path to the working directory and the name of the file being processed.

Get and save the message number

The code in Listing 31 parses the file name to get, save, and display the message number originally used by the POP3 server to identify the message.

Displaying this message number serves as a progress indicator for the user.  This is important because, depending on the speed of the user's Internet service, there can sometime be noticeable pauses between the sending of individual messages to the destination Email address.

            String strMsgNumber =
                   fileName.substring(
                     fileName.indexOf(" "),
                       fileName.lastIndexOf(" "))
                                         .trim();
            msgNumber =
                  Integer.parseInt(strMsgNumber);
            System.out.print(
                           "" + msgNumber + " ");

Listing 31

Process the message

Listing 32 invokes the method named processTheMsg to perform the actual processing of the message.

            processTheMsg();
          }//end for loop on directory length

Listing 32

Listing 32 also signals the end of the for loop.

At this point, I am going to set aside the discussion of the actionPerformed method and discuss the method named processTheMsg.  I will return and finish the discussion of the actionPerformed method later.

The processTheMsg method

The processTheMsg method is shown in its entirety in Listing 33.  The purpose of this method is to initiate the processing of the message.

  void processTheMsg(){
    //Get and save the unique identifier assigned
    // by the pop3Server.
    uidl = pathFileName.substring(
                  pathFileName.lastIndexOf(" "));
    okToDelete = forwardEmailMsg(
                              destinationAddress,
                              smtpServer,
                              fwdTag,
                              pathFileName);
    if(okToDelete){
      msgToDelete.add(pathFileName);
    }//end if
  }//end processTheMsg

Listing 33

The code in Listing 33 forwards the message to the destination Email account and adds the identification of the message to the list of messages scheduled for deletion from the POP3 server later.

The actual forwarding of the message is accomplished by invoking the method named forwardEmailMsg.

Tagging the message

The third parameter to the forwardEmailMsg method can be used to tag the Subject line of the forwarded message to show that it was forwarded, or can be used for any other purpose for that matter.  This parameter value is provided by the user as a command-line parameter to the main method of the BigDog04g class.

If you don't want to tag the message, just pass an empty string, "", as a command-line parameter to the main method of the BigDog04g class when you start the program running.

Prepare to delete messages from POP3 server 

The forwardEmailMsg method returns true if the message was successfully forwarded, and returns false otherwise.

The code in Listing 33 adds the message to a list of messages to be deleted from the POP3 server only if it was successfully forwarded.  If the message was not successfully forwarded, it will not be deleted and will remain on the POP3 server to be processed again later.

The forwardEmailMsg method

This is a fairly complex method.  You can view the code for the method in Listing 46 near the end of the lesson.  You will find a detailed explanation of the method in one of my previously published lessons numbered 2180 and entitled Enlisting Java in the War Against Email Viruses.  Therefore, I won't repeat that explanation here.

Back to the discussion of the actionPerformed method

With the termination of the for loop in Listing 32, all of the messages have been forwarded to the destination Email address.  The remaining code in the actionPerformed method:

  • Enables the button labeled Delete Msg On Server shown in Figure 2.
  • Displays a message to the user in the TextArea shown in Figure 2.
  • Sounds an audible alert to make the user aware that all messages have been forwarded and it is time to make a decision regarding the deletion of the messages from the POP3 server.

This code is shown in Listing 34.

          deleteButton.setEnabled(true);
          textArea.append(
               "Delete msgs from pop3Server?n");
          //Sound an audio alert
          try{
            Toolkit.getDefaultToolkit().beep();
            Thread.currentThread().sleep(500);
            Toolkit.getDefaultToolkit().beep();
            Thread.currentThread().sleep(500);
            Toolkit.getDefaultToolkit().beep();
          }catch(Exception ex){
                           ex.printStackTrace();}
        }//end actionPerformed
      }//end ActionListener
    );//end addActionListener

Listing 34

Listing 34 also signals the end of the anonymous definition and instantiation of the ActionListener object that is registered on the button labeled Forward Msgs shown in Figure 2.

The bottom line on the Forward Msgs button

When the user clicks this button (while it is enabled) all of the messages previously stored in the working directory will be forwarded to the destination Email address.  In addition, a list is populated identifying the messages that were successfully forwarded.  This list will be used later to delete those messages from the POP3 server.

Register another ActionListener object

Continuing with the explanation of the constructor for the BigDog04i class, the code in Listing 35 shows the beginning of the definition, instantiation, and registration of an ActionListener object on the button labeled Delete Msg On Server in Figure 2.

    deleteButton.addActionListener(
      new ActionListener(){
        public void actionPerformed(
                                  ActionEvent e){
          deleteButton.setEnabled(false);
          textArea.append("n");
          System.out.println(
              "nDelete Msgs from POP3 server.");

Listing 35

The primary purpose of this listener object is to do the following when the user clicks this button:

  • Delete the messages from the POP3 server.
  • Move the corresponding message files from the working directory to an archive directory, or optionally simply delete them from the working directory.

By now, the code in Listing 35 should be self-explanatory.

Connect to the POP3 server

In order to delete the messages from the POP3 server, the BigDog04i object must get a connection to the POP3 server from which the messages were originally downloaded.  The code to accomplish this is shown in Listing 36.

          //Get connected to the Email pop3Server
          int port = 110; //pop3 mail port
          try{
            //Get a socket, connected to the
            // specified pop3Server on the
            // specified port.
            socket = new Socket(pop3Server,port);
            //Get an input stream from the socket
            inputStream = new BufferedReader(
                     new InputStreamReader(
                       socket.getInputStream()));
            //Get  an output stream to the socket
            outputStream = new PrintWriter(
               new OutputStreamWriter(
                 socket.getOutputStream()),true);
            //Display the msg received from the
            // pop3Server on the command-line
            // screen immediately following
            // connection.
            String connectMsg =
                               validateOneLine();
            System.out.println(
                    "nConnected to pop3Server "
                                   + connectMsg);
            //The communication process is now in
            // the AUTHORIZATION state.  Send the
            // user name and password to the
            // pop3Server.
            outputStream.println("USER "
                                     + userName);
            //Get response and confirm that the
            // response was +OK and was not -ERR.
            String userResponse =
                               validateOneLine();
            //Display the response on the
            // command-line screen.
            System.out.println("USER "
                                 + userResponse);
            //Send the password to the pop3Server
            outputStream.println("PASS "
                                     + password);
            //Validate the pop3Server's response
            // as +OK. Display the response in
            // the process.
            System.out.println("PASS "
                            + validateOneLine());
          }catch(Exception ex){
                           ex.printStackTrace();}

Listing 36

This code is essentially the same as code that was explained earlier in conjunction with Listing 5 through Listing 10. Therefore, I won't explain it again.

Process the msgToDelete collection

At this point in the execution of the program, the reference variable named msgToDelete contains a reference to a Vector object populated with references to String objects.  Each such String object identifies a message that is to be deleted from the POP3 server.

The deletion is accomplished by executing a for loop that iterates on the contents of the Vector object.  That for loop begins in Listing 37.

          System.out.println(
            "nDeleting Msgs from POP3 server.");
            
          for(int cnt = 0;
                 cnt < msgToDelete.size();cnt++){
            pathFileName = msgToDelete.
                                  elementAt(cnt);
            String strMsgNumber = pathFileName.
             substring(pathFileName.indexOf(" "),
                  pathFileName.lastIndexOf(" ")).
                                          trim();
            int msgNumber = Integer.parseInt(
                                   strMsgNumber);

Listing 37

During each iteration of the for loop, the code in Listing 37 retrieves a string from the collection and parses that string to extract the msgNumber of the message that is to be deleted.

How to actually delete a message

Deletion of a message from the POP3 server is accomplished by marking the message for deletion while in the TRANSACTION state.  The message is marked for deletion by sending a DELE command with the message number as an argument to the command.

The message is actually deleted later when the client sends a QUIT command to the POP3 server causing the POP3 server to enter the UPDATE state.  If the program aborts prematurely before sending a QUIT command, marked messages will not be deleted from the POP3 server.

Temporarily disabling deletion of messages

It is sometimes useful to temporarily disable the deletion of messages from the server while testing or troubleshooting the program.  I will show you how to do this later.

If you elect to temporarily disable the deletion of messages from the POP3 server, you should enable the code shown in Listing 38.  (That code is disabled as shown in Listing 38.)

/*
            //Disable the following statement if
            // you enable message deletion.
            textArea.append(
                  "nMessage deletion disabled");
*/

Listing 38

Mark the message for deletion

The code in Listing 39 uses the DELE command along with the message number to mark a message for deletion.  This code also gets, validates, and displays the POP3 server's response to the DELE command.

            //Disable this code to disable
            // deletion of messages from
            // pop3Server
            outputStream.println("DELE "
                                    + msgNumber);
            //Validate the response and display
            // it on the GUI.
            textArea.append(
                    "Msg: " + msgNumber + " "
                       + validateOneLine()+"n");
            textArea.append(
                  "Deleted:" + msgNumber + "n");
            //Disable to here to disable
            // deletion of messages from
            // pop3Server

Listing 39

The validateOneLine method

Note that the code in Listing 39 invokes a method named validateOneLine to get and validate the server's response as was the case with the object of type BigDog04g.

Disabling the deletion of messages

If you elect to disable the deletion of messages from the POP3 server, you should disable all of the code in Listing 39.

Handling local message files

Once you have forwarded the messages to the destination Email address, you can either use a method named moveFile to move the local message files from the working directory into an archive directory, or you can simply delete them from the working directory.  You must make this decision and appropriately enable and disable certain blocks of code prior to compilation of the program.

Code for handling local message files

The code to accomplish this is shown in Listing 40, and should be self-explanatory.

            //To move message files to the
            // archive directory, enable this
            // block of code and disable the next
            // block of code.
            {//Start move block
              moveFile(pathFileName,archiveDir);
            }//End move block
/*
            //To delete message files without
            // saving them in an archive
            // directory, enable this block of
            // code and disable the previous
            // block of code.
            {//Start delete block
              File file = new File(pathFileName);
              boolean isDeleted = file.delete();
              if(!isDeleted)System.out.println(
                     "Unable to delete " + file);
            }//End delete block
*/
          }//end for loop on msgToDelete.size()

Listing 40

This version saves the message files

The version of the program shown in Listing 40 moves the message files from the working directory into the archive directory.  If you do this, you should manually delete the files in the archive directory on a regular schedule to free up disk space.

Listing 40 also signals the end of the for loop that iterates on the list of messages to be deleted.

Terminate the POP3 server session

The code in Listing 41 sends a QUIT command to the POP3 server to terminate the session.  This code is essentially the same as that discussed earlier in conjunction with Listing 21.

          System.out.println(
                  "Disconnect from POP3 server");
          outputStream.println("QUIT");
          String quitResponse =
                               validateOneLine();
          //Display the response on the
          // command-line screen.
          System.out.println(
                         "QUIT " + quitResponse);
          //Server is now in the UPDATE mode.
          // It will delete all files marked
          // with the DELE command earlier
          // in the execution of the program.
          //Close the socket
          try{
            socket.close();
          }catch(Exception ex){
            ex.printStackTrace();
          }//end catch
          textArea.append("nnMessages deleted "
               + "from pop3Server.n"
               + "Press X button to terminate.");
        }//end actionPerformed
      }//end ActionListener
    );//end addActionListener

Listing 41

One operational difference

There is one operational difference that isn't apparent in Listing 41.  In the earlier case shown in Listing 21, messages had been downloaded from the server but were not marked for deletion.  Therefore, those messages remained on the server after the session was terminated.  In this case, messages have been marked for deletion, and will be deleted by the server when the session is properly terminated.

Listing 41 also signals the end of the anonymous definition, instantiation, and registration of an ActionListener object on the button labeled Delete Msg On Server in Figure 2.  When this button has been enabled and is clicked by the user, the messages that have been identified in the Vector object referred to by msgToDelete will be deleted from the POP3 server.

Configure the GUI

The remaining code in the constructor for the class named BigDog04i is shown in Listing 42.

    add(forwardButton);
    add(deleteButton);
    deleteButton.setEnabled(false);
    add(textArea);
    textArea.setText("");
    setLayout(new FlowLayout());
    setTitle("Copyright 2005, R.G.Baldwin");
    setBounds(250,0,250,200);
    //Make the GUI visible.
    setVisible(true);
  }//end constructor

Listing 42

This code configures the GUI shown in Figure 2 by placing components on the Frame object, setting the size, and making it visible.

The moveFile method

Listing 43 shows the moveFile method in it entirety.  This method is used in Listing 40 to move a file from its current location specified by pathFileName to a new location specified by archiveDir.

  public void moveFile(String pathFileName,
                       String archiveDir){
    String fileName = pathFileName.substring(
              pathFileName.lastIndexOf('/') + 1);
    String archiveDirFileName =
                           archiveDir + fileName;
    boolean moved =
                 new File(pathFileName).renameTo(
                   new File(archiveDirFileName));
    if(!moved)System.out.println(
      "Unable to move " + new File(pathFileName)
      + "nto " + new File(archiveDirFileName));
  }//end moveFile method
}//end class BigDog04i

Listing 43

If the code in Listing 43 is unfamiliar to you, I have explained code similar to this in numerous previously published lessons.

Listing 43 also signals the end of the definition of the class named BigDog04i.





Page 1 of 2



Comment and Contribute

 


(Maximum characters: 1200). You have characters left.

 

 


Sitemap | Contact Us

Rocket Fuel