Java Programming Notes # 2402
- Preface
- General
Background Information - Discussion and
Sample Code - Run the Program
- Summary
- Complete Program
Listings
Preface
Multiple Email accounts?
In Part 1 of this lesson, I asked the following questions:
- 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?
I then went on to provide and explain an interactive Java program
that you can use to consolidate all of
your Email messages from different accounts onto a single Email server.
If the answer to the first question above was more than one Email account,
and the answer to the last question was no, then either the program that
I provided in
Part 1 or the program that I will provide in this lesson may be of
considerable interest to you.
The difference between the two programs
This lesson provides a non-interactive version of the program from
Part 1 that you can use
to consolidate all of
your Email messages onto a single Email server on a scheduled, unattended, automatic basis.
By non-interactive, I mean that the difference between this program and the program
from Part 1 is:
- The program in
Part 1 requires user interaction each time messages are
moved from one Email server to another. - The program that I will present in this lesson is a “set it and
forget it” program.
Set it and forget it
In other words, you can set this program up
as a Scheduled Task in a task scheduler so that it will run automatically on whatever
schedule your task scheduler supports.
You can set up different instances of this program as separate Scheduled
Tasks to
automatically consolidate Email messages from different Email accounts onto a
single Email server, even if the managers of those accounts refuse to forward
your Email messages for you.
If you also need to consolidate Email messages on a demand (non-scheduled) basis,
you can accomplish that with a couple of mouse clicks.
As a result, by using this program, you will have an easy way to consolidate Email messages
from all of your Email accounts onto a single Email server so that you only have to
check one Email account in order to read all of your Email? Then you can
answer yes to the third question that I posed earlier.
Third lesson in a series
This is the third lesson in a series of lessons on the general topic of
moving Email messages among servers and local computers.
The first lesson was entitled
Consolidating Email using Java.
That is the document that I will refer to as
Part 1 of this lesson.
The second and previous lesson was entitled
Uploading Old Email to Gmail using
Java.
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
Multiple Email accounts
The previous two lessons in this series discussed the background, the
desirability, and the
rationale for using a program like the one that I will present in this lesson to
consolidate Email messages from multiple Email accounts onto a single Email
server. I won’t repeat that discussion here. Rather, this lesson
will concentrate on the technical aspects of the automated version of a
program for performing such consolidation.
Even if you decide that you would prefer to use the automated version of the
program presented in this lesson, it is strongly recommended that you study
Part 1 before embarking on this lesson. The code in the program in this lesson is
very similar to, but organized differently from the code in
Part 1.
Therefore, I won’t explain the
details of the code in this lesson. Rather, I will simply refer you to the
appropriate section of
Part 1 for detailed explanations of the code.
Discussion
and Sample Code
An Email forwarding program
In this lesson, I will provide 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 server on a totally
unattended, automated, and scheduled basis:
- BigDog06fetch
- BigDog06forward
The first class listed above replaces the class named BigDog04g in
Part 1. The second class listed above replaces the class named
BigDog04i in
Part 1.
Command-line parameters
As was the case in
Part 1, all of the user-specific information required by the program is provided
by the following eight command-line parameters:
- pop3Server: Server from which the messages will be
downloaded - userName: On pop3Server
- password: On pop3Server
- destinationAddress: Email address to which the messages
will be forwarded - smtpServer: An available SMTP server that can be used to
forward the messages - workingDir: Messages are temporarily stored here
- archiveDir: Messages are moved to here from the workingDir
for long-term storage if desired (optional) - fwdTag: String prepended to the subject when the message is
forwarded. (Can be an empty string.)
Automation was my long-term goal
As I mentioned in
Part 1, I’m aware that parameter input via 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. Command-line input can be automated using batch files.
Unattended automated execution of the program was my long-term goal
for this project from the very beginning.
Doing the setup for unattended automated execution
In the next few paragraphs, I will explain how to use batch files to schedule
the execution of the program under the Windows XP operating system. If you
are running under a different operating system, you will need to examine the
documentation for that operating system to learn how to schedule tasks on your
machine.
One batch file for each POP3 Server
I have a different batch file set up for each of the POP3 servers from which I want
messages to be downloaded and consolidated onto a common Email server.
(I use Google’s Gmail as my common Email account.)
The Windows XP Scheduled Tasks feature
For a particular POP3 server, all I have to do to cause the messages to be
moved from the POP3 server to my Gmail
account is to register the batch file for that POP3 server as a Scheduled Task under Windows XP.
(Actually I have two batch files set up for each POP3 server because I
have to use a different SMTP server depending on whether I am at home with
my laptop or at work with my laptop. (See Figure 2.)
I normally have half of the Scheduled Tasks disabled when I am at home, and
have the other half disabled when I am at work.)
What is the Scheduled Tasks feature?
Figure 1 contains part of what the Microsoft help system has to say about the
Scheduled Tasks feature.
Scheduled Tasks overviewWith Scheduled Tasks, you can schedule any script, program, or With Scheduled Tasks, you can also:
Figure 1 |
The idle time option is missing
One thing that the Microsoft help system fails to mention is that the
Scheduled Tasks feature also allows tasks to be scheduled for execution anytime
following a specified amount of idle time. I have my system set up so that
each of the batch files is executed as a Scheduled Task after a few minutes of
idle time.
(As you will see shortly, this idle-time capability is also missing from the
Windows Scheduled
Tasks Wizard. However, there is a workaround that can be used to
incorporate the idle-time capability.)
Icons on the desktop
I also have icons with shortcuts to each of the batch files on my desktop so
that I can force a batch file to be executed whenever I want even if such
execution isn’t scheduled to occur any time soon.
Registering Scheduled Tasks
To schedule tasks under Windows XP, go to the Control Panel and double click
on Scheduled Tasks.
When the Scheduled Tasks folder appears, (see
Figure 2), double click on “Add
Scheduled Task.” This will start the Scheduled Task Wizard
running.
Just follow the instructions provided by the wizard and browse to the
appropriate batch
file when you are given the opportunity to Select Program to Schedule.
Idle time option is missing from the wizard also
Be aware, however, that in the version of Windows XP that I am currently
running (Windows XP Professional, Version 5.1, Service Pack 2), there is
an apparent bug in the Scheduled Task Wizard. In particular, when
using the wizard to set up a Scheduled Task, you are only given the following
six choices as to when the task should be performed:
- Daily
- Weekly
- Monthly
- One time only
- When my computer starts
- When I log on
The ability to cause the task to be performed during idle time is
missing from the list.
Can correct for the omission later
However, even though it is inconvenient, if you go ahead and select one of
the items in the list and click Next a couple of times, you will reach the point where you can check a box that reads “Open advanced
properties for this task when I click Finish.” The
Advanced
Properties dialog (see Figure 3) will let you change your selection to When idle if
you choose to do so.
When you check that box and click Finish, the Advanced Properties
dialog having the following three tabs (see Figure 3,
Figure 4, and Figure 5) will appear:
- Task
- Schedule
- Settings
A more-complete list
Select the Schedule tab shown in Figure 4 and you will have access to a pull-down list
entitled Schedule Task that contains the items in the list given above.
The pull-down list also provides an additional selection that reads “When
idle.”
If that is your choice, make that selection and then select the Settings
tab shown in Figure 5. The dialog on the Settings tab will give you an opportunity to
customize the selection in more detail.
Scheduled Tasks on the desktop
Because you may want to make modifications later to tasks that you have scheduled,
it is convenient to go to the Control Panel and create a shortcut on the desktop
to Scheduled Tasks. You can do this by right-clicking on Scheduled
Tasks on the Control Panel and then selecting Create Shortcut.
Having created the Scheduled Tasks shortcut on the desktop, whenever you double-click the shortcut,
the Scheduled Tasks folder should
appear on your screen, looking something like that shown in Figure 2.
Figure 2 |
Creating a new Scheduled Task
To create a new Scheduled Task, double-click on the top item in Figure
2 that reads Add Scheduled Task. This will start the Scheduled
Task Wizard that I discussed earlier.
Existing Scheduled Tasks
Each of the bottom four items in Figure 2 represent scheduled tasks.
Those with the red x are temporarily disabled.
Modifying a Scheduled Task
Once you have created a Scheduled Task, you can modify that task (or use
it to create a new one) without having to deal with the wizard. If you
right-click on a Scheduled Task in Figure 2, a popup menu will appear with the following
selections:
- Run
- Cut
- Copy
- Delete
- Rename
- Properties
The first five choices are generally self-explanatory. For example, you
can cause the task to run at a non-scheduled time by selecting Run on the
popup menu.
Using an existing task to create a new one
You can create a new Scheduled Task by:
- Copying an existing Scheduled Task to the clipboard
- Pasting it into a different directory
- Renaming it in the other directory
- Copying it back into the Scheduled Tasks directory
- Modifying the properties of the new Scheduled Task to suit the new
requirements
The Scheduled Task Properties
Selecting Properties in the popup menu produces the dialog shown in
Figure 3. This is the dialog that I referred to as
the Advanced Properties dialog earlier.
Figure 3 |
Modifying a Scheduled Task
Once you have opened the Advanced Properties dialog on a Scheduled
Task and have selected one
of the tabs, you can make any modification to the task that
is allowed by the system.
Disable and/or enable a Scheduled Task
For example, the checkbox in the bottom left
corner of Figure 3 can be used to enable and disable the Scheduled Task.
(Figure 2 shows two Scheduled Tasks that have been disabled.)
The Schedule and Settings tabs
Figure 5 |
There are no GUIs with this version of the program
That’s enough talk about Windows. Let’s discuss Java programming.
This version of the program does not display GUIs on the screen as was the
case with the version presented in
Part 1. Rather, this version runs in
batch mode, from start to finish, with no user interaction.
From start to finish …
From start to finish includes the following operations:
- Download messages and save them as individual files in a local working
directory. - Transform the message files into Email messages and send the messages to
the specified destination Email address. - Upon confirmation that each message has been sent successfully, delete
the original message from the POP3 server, and remove the message file from
the working directory.
If a problem is recognized anywhere along the way, the messages are not
deleted from the POP3 server and the message files are not removed from the
working directory. This makes it possible to recover from the problem, if
necessary, by downloading the messages from the POP3 server into an ordinary
Email client program.
Status and progress information
Status and progress information appears in the command-line window while the
program is running. However, unless you put a PAUSE command at the end of
the batch file, that window will probably disappear before you
have much time to study it.
(If you want the command-line window to remain
visible until you dismiss it, put a PAUSE command at the end of the batch file. This might
be useful if you suspect that there is a problem, but it will probably also
prevent the next scheduled running of the batch file and the program.)
A batch-file command
A typical batch file command is shown in Figure 6.
(Note that it was
necessary for me to manually enter line breaks into Figure 6 to force the command
to fit in this narrow publication format.)
java BigDog06fetch pop.austincc.edu userName password acct@xyz.com smtp-server.austin.rr.com ./temp/ c:/z-MailArchives/ "ACC-" Figure 6 |
You should be able to match up the individual command-line parameters in
Figure 6 with the description of the parameters discussed earlier.
Two classes are used
This program consists of the following two classes:
- BigDog06fetch
- BigDog06forward
As mentioned earlier, the BigDog06fetch class replaces the class named BigDog04g
in Part 1. The BigDog06forward class replaces the class named
BigDog04i in
Part 1.
Because the user interactivity
has been removed, the two classes presented here are much simpler than the two
classes presented in
Part 1.
Overall control
The class named BigDog06fetch is a Java application and serves as
the driver for the entire program. The main method instantiates an
object of the BigDog06fetch class.
The BigDog06fetch object 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
BigDog06forward.
The object of the class
BigDog06forward
- Forwards the messages to a specified Email address.
- Deletes the messages from the POP3 server.
- Removes the message files from the working directory.
Deleting messages and moving message files
Messages are not deleted from the POP3 server until they have been
successfully forwarded to the specified Email address. 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.
Directories must already exist
Note that both of the directories specified by
command-line parameters 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 command line 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 command-line 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 1725 at
http://www.cse.ohio-state.edu/cgi-bin/rfc/rfc1725.html
A POP3 Command Summary is provided in the program comments in Listing 9 near
the end of the lesson.
Program testing
The entire 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, (but not very much)
Complete listings of both classes are provided in Listing 9 and Listing 10 near the end of the lesson.
Normally, I would break both classes down into fragments and discuss the
fragments in detail.
However, because of the similarities between these classes and the classes that I
explained in Part 1, my discussion of code in this lesson will be very
limited. I will concentrate on those aspects of the code in this lesson
that are different from the code in
Part 1.
BigDog06fetch is not a Frame
Unlike the class definition for BigDog04g in
Part 1, the class
definition for BigDog06fetch is not a Frame. That is because
this version of the program doesn’t present a GUI to the user. The class
definition for BigDog06fetch begins in Listing 1.
class BigDog06fetch{ static String workingDir; int numberMsgs = 0; int msgNumber; String uidl = "";//unique msg ID BufferedReader inputStream; PrintWriter outputStream; Socket socket; String pathFileName; static String[] params; |
The code in Listing 1 simply declares some necessary variables.
The main method
BigDog06fetch 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 BigDog06fetch " + "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 |
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]; |
(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 BigDog06fetch object
Finally, as shown in Listing 4, the main method displays the
command-line parameters and then uses the first three command-line parameters to instantiate an object of the
BigDog06fetch class.
System.out.println( "pop3Server: " + params[0]); System.out.println("userName: " + params[1]); System.out.println("password: .........."); System.out.println( "destinationAddress: " + params[3]); System.out.println( "smtpServer: " + params[4]); System.out.println( "workingDir: " + params[5]); System.out.println( "archiveDir: " + params[6]); System.out.println("fwdTag: " + params[7]); new BigDog06fetch(args[0],args[1],args[2]); }//end main |
Once constructed, this object will download and save the messages on the
specified POP3 Email server as individual files in the local working directory.
Instantiate a BigDog06forward object
The BigDog06fetch object will, in turn, instantiate an object of the
BigDog06forward class, which will forward the messages in the working directory to the destination
Email address. The
BigDog06forward object will also delete the messages from the POP3 server
and remove the message files from the working directory.
The BigDog06fetch constructor
The BigDog06fetch constructor is shown (with most of the code removed
for brevity) in Listing 5. The constructor consists of two main parts.
The first part gets a connection to the POP3 Email server using the three
incoming parameters. This part is essentially the same as the
corresponding code in the constructor for the program in
Part 1.
BigDog06fetch(String pop3Server, String userName, String password){ //First part int port = 110; //pop3 mail port try{ //Code removed for brevity }//end try catch(Exception e){ e.printStackTrace(); System.exit(0); }//end catch //Second part fetchMsgs(); }//end constructor |
The second part of the constructor
The second part, shown in boldface near the end of Listing 5, invokes the
method named fetchMsgs to download the messages from the POP3 server and
to save them as individual files in the working directory.
Code to get a connection
I explained all of the code involved in getting the connection to the POP3 server
in Part 1, and won’t repeat that explanation here.
The fetchMsgs method
The fetchMsgs method is new to this lesson, although most of the code
in the method appeared in a method named actionPerformed in
Part 1.
Don’t register a WindowListener object
Because the constructor in
Part 1 created a GUI, it registered a
WindowListener object on the Frame. There is no Frame in
this version of the program, and hence no WindowListener object.
Don’t instantiate GUI objects
For similar reasons, the code in this constructor doesn’t instantiate Button and TextArea
objects as was the case in
Part 1.
Don’t register an ActionListener object
Also, for the same reason, there is no reason for the code in this version of
the program to register an ActionListener object on a Button
(there is no button).
Where did the work get done in
Part 1?
In the version of the program in
Part 1, the code in the actionPerformed
method belonging to the ActionListener object did the work of downloading
the messages from the POP3 server and writing those messages into local files in
the working directory.
Where does the work get done in this version?
In this version of the program, the code to do that
work is contained in the method named fetchMsgs.
The difference is that
in the version in
Part 1, once the connection to the POP3 server was
made, message downloading was deferred until the user clicked a Button in
the GUI, thus invoking the actionPerformed method. In this version, as soon as the connection is made, the
fetchMsgs method is invoked by the constructor causing message downloading to begin
immediately.
The fetchMsgs method
The fetchMsgs method is shown in Listing 6 with most of the code
omitted for brevity. (You can view all of the code for the fetchMsgs
method in Listing 9 near the end of the lesson.)
void fetchMsgs(){ //Download all messages on the pop3Server. try{ //The communication process is now in the // TRANSACTION state. //Retrieve and save messages //Code deleted for brevity //All messages have been downloaded and // saved as local files. Instantiate an // object that will forward each of the // messages to the specified email // address. new BigDog06forward( 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(); System.exit(0); }//end catch }//end fetchMsgs //===========================================// }//end class BigDog06fetch //=============================================// |
Same as the actionPerformed method
The code in the fetchMsgs method in this version is essentially the
same as the code in the actionPerformed method in the version in
Part 1. I explained that code in detail in that lesson, and won’t
repeat that explanation here.
Instantiate an object of the BigDog06forward class
Note that in addition to downloading the messages, the code in Listing 6 also
instantiates an object of the BigDog06forward class to cause the messages
to be sent to the destination Email address.
Listing 6 also signals the end of the definition of the BigDog06fetch
class.
The BigDog06forward 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 BigDog06forward class is instantiated by the code in
Listing 6 to tag and forward a set of message files that were written into the working
directory by the
BigDog06fetch object.
Tag and forward the messages
This BigDog06forward object tags the messages with a tag defined by the user
as a command-line parameter to the main method of the class named
BigDog06fetch. 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.
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 temporarily disabled for this purpose.)
The BigDog06forward class
The class definition for the BigDog06forward class begins in Listing 7. As you can see, the class
does not extend Frame, as was the case for the version of the program in
Part 1 Once again, this version of the program does not present
GUIs to the user, and therefore there is no reason for the class to extend
Frame.
class BigDog06forward{ //User-specific information is stored here. // This information is provided in the form of // constructor parameters. //ID of the destination email account. final String destinationAddress; //An smtp server through which the user is // authorized to send email messages. final 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 |
The code in Listing 7 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 the BigDog06forward class
The constructor is shown in its entirety in Listing 8.
BigDog06forward(final String pop3Server, final String userName, final String password, final String destinationAddress, final String smtpServer, final String workingDir, final String archiveDir, final String fwdTag){ this.pop3Server = pop3Server; this.userName = userName; this.password = password; this.destinationAddress = destinationAddress; this.smtpServer = smtpServer; this.workingDir = workingDir; this.archiveDir = archiveDir; this.fwdTag = fwdTag; System.out.println("Forwarding messages:"); forwardMsgs(); deleteMsgs(); }//end constructor |
Much shorter than in
Part 1
The constructor for this version of the program is much shorter than was the
case for the version of the program in
Part 1. That is because the
earlier version presented a GUI to the user with two Button objects and a
TextArea object.
Forwarding the messages
One of the buttons was used to initiate and handle the forwarding of messages to the
specified destination Email address.
An ActionListener object was
registered on that button, and the actionPerformed method belonging to
that ActionListener object did all of the work.
That same work is done
in this program by the method named forwardMsgs that is invoked near the
end of the constructor in Listing 8.
The forwardMsgs method
The code in the forwardMsgs method is essentially the same as the code
in the actionPerformed method that it replaces. I explained that
code in Part 1 and won’t repeat that explanation here. You can
view the forwardMsgs method in Listing 10 near the end of this lesson.
Deleting messages
The other button in the GUI in
Part 1 was used to initiate and handle the deletion of email messages from the
POP3 server.
An ActionListener
object was registered on that button, and the actionPerformed method
belonging to that ActionListener object did all of the work.
That same work
is done in this program by the method named deleteMsgs that is invoked
near the end of the constructor in Listing 8.
The deleteMsgs method
The code in the deleteMsgs method is essentially the same as the code
in the actionPerformed method that it replaces. Once again, I
explained that code in
Part 1 and won’t repeat that explanation here.
You can view the deleteMsgs method in Listing 10 near the end of this
lesson.
Run the Program
I encourage you to copy the code from Listings 9 and 10 into your text
editor, compile it, and execute it. Experiment with it, making
changes, and observing the results of your changes.
I do recommend, however, that before compiling and executing the code, you make the changes
necessary to prevent messages from being deleted from your POP3
server. That way, if something goes wrong, the messages will still be
there for you to access using your regular Email client program. Once you
are satisfied with the behavior of the program and are ready to use it on a
routine production basis, you can re-enable the deletion of messages from your
POP3 server.
Also please be aware of the following disclaimer:
THIS PROGRAM IS PROVIDED TO YOU AT NO COST. BY USING THIS PROGRAM TO
PROCESS YOUR EMAIL, YOU AGREE THAT YOU ARE USING IT AT YOUR OWN RISK. THE
AUTHOR OF THE PROGRAM, RICHARD G. BALDWIN, ACCEPTS NO LIABILITY FOR ANY LOSS
THAT YOU MAY INCUR THROUGH THE USE OF THIS PROGRAM.
Summary
In this lesson, I provided and explained two Java
classes that work together as a program to download Email messages from a
POP3 server and to forward those messages to another Email server.
Such a program is particularly useful for consolidating all of your Email
messages onto a single Email server so that you only need to check your mail on
one server to see all of your messages.
I also
showed you how to register the program as a Scheduled Task under Windows XP to
cause the program to run automatically in an unattended mode on a schedule that you specify.
Complete Program Listings
Complete listings of the classes discussed in this lesson are shown in Listings
9 and 10 below.
/*File BigDog06fetch.java Copyright 2005, R.G.Baldwin Rev 08/17/05 This is a modification of the program named BigDog04g to fully automate the process. DISCLAIMER: THIS PROGRAM IS PROVIDED TO YOU AT NO COST. BY USING THIS PROGRAM TO PROCESS YOUR EMAIL, YOU AGREE THAT YOU ARE USING IT AT YOUR OWN RISK. THE AUTHOR OF THE PROGRAM, RICHARD G. BALDWIN, ACCEPTS NO LIABILITY FOR ANY LOSS THAT YOU MAY INCUR THROUGH THE USE OF THIS PROGRAM. This program downloads all messages from a pop3Server and writes them as individual files in a local working directory. Then it instantiates an object of the class BigDog06forward, which forwards the messages to a specified email address using a specified SMTP server. Messages are not deleted from the pop3Server until they have been forwarded to the specified email address. When the messages are deleted from the pop3Server, the message files are also removed from the working directory. By enabling and disabling blocks of code prior to compilation, you can customize the program to cause the message files to be moved from the working directory into an archive directory or simply deleted from the working directory without saving them in an archive directory. The following information is provided as command-line parameters: pop3Server: server from which the messages will be downloaded userName: on pop3Server password: on pop3Server destinationAddress: email address to which the messages are to be forwarded smtpServer: an available SMTP server that can be used to forward the messages workingDir: messages are temporarily stored here archiveDir: messages are moved to here from the workingDir for long-term storage if desired fwdTag: String prepended to the subject when the message is forwarded. 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, but it must be provided. If you don't want to tag the subject of the forwarded msg, just enter "" as the eighth command-line parameter. For technical information on POP3, see RFC 1725 at http://www.cse.ohio-state.edu/cgi-bin/rfc/ rfc1725.html A POP3 Command Summary follows based on the information at that web site. Minimal POP3 Commands: USER name PASS string QUIT STAT LIST [msg] RETR msg DELE msg NOOP RSET QUIT Optional POP3 Commands: APOP name digest TOP msg n UIDL [msg] POP3 Replies: +OK -ERR Tested using JDK 1.5.0_01 under WinXP ************************************************/ import java.net.*; import java.io.*; import java.awt.*; class BigDog06fetch{ static String workingDir; int numberMsgs = 0; int msgNumber; String uidl = "";//unique msg ID BufferedReader inputStream; PrintWriter outputStream; Socket socket; String pathFileName; static String[] params; public static void main(String[] args){ if(args.length != 8){ System.out.println( "Wrong number of parameters"); System.out.println( "Usage: java BigDog06fetch " + "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 //Save the command-line parameters for later //use by the object of the class // BigDog06forward params = args; //Establish the name of the working // directory. Note that this directory must // already exist. workingDir = args[5]; System.out.println( "pop3Server: " + params[0]); System.out.println("userName: " + params[1]); System.out.println("password: .........."); System.out.println( "destinationAddress: " + params[3]); System.out.println( "smtpServer: " + params[4]); System.out.println( "workingDir: " + params[5]); System.out.println( "archiveDir: " + params[6]); System.out.println("fwdTag: " + params[7]); //Instantiate an object that will download // and save the messages as individual files. // This object will in turn instantiate an // object of the class BigDog06forward, which // will forward the messages to the // destination email address. new BigDog06fetch(args[0],args[1],args[2]); }//end main //===========================================// //Constructor BigDog06fetch(String pop3Server, String userName, String password){ 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. // Note that this stream will autoflush. 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( "Connected to pop3Server " + connectMsg); //The communication process is now in the // AUTHORIZATION state. Send the user // name and password to the pop3Server. //Commands are sent in plain text, upper // case to the pop3Server. Some commands // require an argument following the // command, as is the case with USER. //Send the command. 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()); System.out.println("Downloading Messages"); }catch(Exception e){ e.printStackTrace(); System.exit(0); }//end catch fetchMsgs(); }//end constructor //===========================================// //Validate a one-line response. //The purpose of this method is to confirm that // the pop3Server returned +OK and not -ERR to // the previous command. //If +OK, the method returns the string // returned by the pop3Server. //If -ERR, the method displays the string // returned by the pop3Server and terminates // the session. 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(); System.exit(0); }//end catch //The following return statement is required // to satisfy the compiler, but it can never // be reached. return "Make compiler happy"; }//end validateOneLine() //===========================================// void fetchMsgs(){ //Download all messages on the pop3Server. try{ //The communication process is now in the // TRANSACTION state. //Retrive and save messages 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); if(numberMsgs == 0){ System.out.println( "No msg to download."); System.out.println( "Terminating program."); System.exit(0); }//end if //NOTE: Msg numbers begin with 1, not 0. //Retrieve and save each message. Each msg // ends with a period on a new line. msgNumber = 1; while(msgNumber <= numberMsgs){ //Process the next message. //Get and save a unique identifier for // the message from the pop3Server and // validate the response. outputStream.println( "UIDL " + msgNumber); uidl = validateOneLine(); //Open an output file to save the // message. Use the UIDL as the file // name. pathFileName = workingDir + uidl; DataOutputStream dataOut = new DataOutputStream( new FileOutputStream( pathFileName)); //Send a RETR command to begin the // message retrieval process outputStream.println( "RETR " + msgNumber); //Validate the response. String retrResponse = validateOneLine(); //Read the first line in the message from // the pop3Server. String msgLine = inputStream.readLine(); //Continue reading lines until a "." is // encountered as the first char in a // line. That signals the end of the // msg. while(!(msgLine.equals("."))){ //Write the line to the output file and // read the next line. Insert newline // characters when writing the output // to the file. dataOut.writeBytes(msgLine + "n"); msgLine = inputStream.readLine(); }//end while(!(msgLine.equals(".") //Close the output file. The message is // now stored in a local file with a file // name based on the unique ID provided // by the pop3Server. dataOut.close(); //Show progress on the command-line // screen System.out.print(msgNumber + " "); //Increment the message number in // preparation for processing the next // message. msgNumber++; }//end while(msgNumber <= numberMsgs) System.out.println( "nMessages downloaded"); //Terminate the session with the // pop3Server. System.out.println( "nDisconnect from pop3Server."); outputStream.println("QUIT"); String quitResponse = validateOneLine(); //Display the response on the // command-line screen. System.out.println("QUIT " + quitResponse); System.out.println();//blank line socket.close(); //Sound an alarm. try{ Thread.currentThread().sleep(500); Toolkit.getDefaultToolkit().beep(); Thread.currentThread().sleep(500); Toolkit.getDefaultToolkit().beep(); Thread.currentThread().sleep(500); }catch(Exception ex){ ex.printStackTrace(); System.exit(0); }//end catch //All messages have been downloaded and // saved as local files. Instantiate an // object that will forward each of the // messages to the specified email // address. new BigDog06forward( 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(); System.exit(0); }//end catch }//end fetchMsgs //===========================================// }//end class BigDog06fetch //=============================================// Listing 9 |
/*File BigDog06forward.java Copyright 2005, R.G.Baldwin Rev 08/17/05 DISCLAIMER: THIS PROGRAM IS PROVIDED TO YOU AT NO COST. BY USING THIS PROGRAM TO PROCESS YOUR EMAIL, YOU AGREE THAT YOU ARE USING IT AT YOUR OWN RISK. THE AUTHOR OF THE PROGRAM, RICHARD G. BALDWIN, ACCEPTS NO LIABILITY FOR ANY LOSS THAT YOU MAY INCUR THROUGH THE USE OF THIS PROGRAM. An object of this class is instantiated by an object of the class named BigDog06fetch to process a set of message files written by the BigDog06fetch object. This object tags messages with a tag defined by the user as a command-line parameter to the main method for the class named BigDog06fetch. Then the object forwards the messages to a destination email account that is specified as a command-line parameter using an SMTP server that is specified as a command-line parameter. See additional comments at the beginning of BigDog06fetch.java. This program 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 disabled for test purposes by disabling a block of code before compiling the program. Certain portions of this program may have been disabled for test purposes. Search for the word disable to identify those portions. Tested using JDK 1.5.0_01 under WinXP ************************************************/ import java.net.*; import java.io.*; import java.util.*; import java.awt.*; //import java.awt.event.*; import sun.net.smtp.SmtpClient; class BigDog06forward{ //User-specific information is stored here. // This information is provided in the form of // constructor parameters. //ID of the destination email account. final String destinationAddress; //An smtp server through which the user is // authorized to send email messages. final 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>(); String uidl; boolean okToDelete = false; int msgNumber = 0; String pop3Server; String userName; String password; //===========================================// //Constructor BigDog06forward(final String pop3Server, final String userName, final String password, final String destinationAddress, final String smtpServer, final String workingDir, final String archiveDir, final String fwdTag){ this.pop3Server = pop3Server; this.userName = userName; this.password = password; this.destinationAddress = destinationAddress; this.smtpServer = smtpServer; this.workingDir = workingDir; this.archiveDir = archiveDir; this.fwdTag = fwdTag; System.out.println("Forwarding messages:"); forwardMsgs(); deleteMsgs(); }//end constructor //===========================================// //Validate a one-line response. //The purpose of this method is to confirm that // the pop3Server returned +OK and not -ERR to // the previous command. //If +OK, the method returns the string // returned by the pop3Server. //If -ERR, the method displays the string // returned by the pop3Server and terminates // the session. 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 e){ e.printStackTrace(); System.exit(0); }//end catch //The following return statement is requied // to satisfy the compiler, but it can never // be reached.. return "Make compiler happy"; }//end validateOneLine() //===========================================// //Method 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 //===========================================// //This method reads and saves lines of data // from a file starting with the line that // startsWith firstLine and ending with the // line that startsWith lastLine. //If firstLine is null, data is saved beginning // with the first line in the file. //If lastLine is null, data is saved to the end // of the file. //The lines of data from the file are saved by // concatenating them into a single string with // a newline inserted into the string at the // end of each line. //The name and path to the file is given by // pathFileName. public String readLines(String pathFileName, String firstLine, String lastLine){ StringBuffer strBuf = new StringBuffer(); try{ BufferedReader inDataMsg = new BufferedReader(new FileReader( pathFileName)); String data; boolean isSave = false; while((data = inDataMsg.readLine()) != null){ if( ((firstLine == null) || (data.startsWith(firstLine))) && (isSave == false)){ isSave = true; }//end if if(isSave){ strBuf.append(data + "n"); }//end if if((lastLine != null) && (data.startsWith(lastLine))){ break;//no need to read any more }//end if }//end while loop inDataMsg.close();//Close file }catch(Exception e){ e.printStackTrace(); System.exit(0); }//end catch return new String(strBuf); }//end readLines //===========================================// //This method is used to construct an email // message and send it to the // destinationAddress. public boolean forwardEmailMsg( String destinationAddress, String smtpServer, String fwdTag, String pathFileName){ StringBuffer message = new StringBuffer( "No message found"); 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(destinationAddress); //Pass the email address of the // destinationAddress to the to() method. smtp.to(destinationAddress); //Construct the message as a single // StringBuffer object by concatenating // all of the lines in the message file. message = new StringBuffer(readLines( pathFileName,null,null)); //Insert fwdTag in subject message = message.insert(message.indexOf( "Subject: ")+9,fwdTag); //Get an output stream for the message PrintStream msg = smtp.startMessage(); //Write the message into the output // stream. msg.println(new String(message)); //Close the stream and send the message smtp.closeServer(); return true; }catch( Exception e ){ e.printStackTrace(); System.out.println( "while forwarding email"); //Sound an alarm. try{ Thread.currentThread().sleep(500); Toolkit.getDefaultToolkit().beep(); Thread.currentThread().sleep(500); Toolkit.getDefaultToolkit().beep(); Thread.currentThread().sleep(500); }catch(Exception ex){ System.out.println(ex); }//end catch //Return false to indicate that the msg // was not successfully forwarded. return false; }//end catch }//end forwardEmailMsg //===========================================// void forwardMsgs(){ //Get a directory listing //The following code creates a directory // listing containing only those files 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 //Now process the files in the directory for(int msgCounter = 0; msgCounter < dirList.length; msgCounter++){ String fileName = dirList[msgCounter]; pathFileName = workingDir + fileName; //Get the original message number used by // the pop3Server to ID the msg. String strMsgNumber = fileName.substring( fileName.indexOf(" "), fileName.lastIndexOf(" ")).trim(); msgNumber = Integer.parseInt(strMsgNumber); System.out.print("" + msgNumber + " "); //This code 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 pop3Server later. The third // parameter can be used to tag the message // to show that it was forwarded. This // parameter is provided as a command-line // parameter to the BigDog06fetch program. // Just pass an empty string, "", if you // don't want to tag it. //Don't add the message to the deletion // list if forwarding failed. okToDelete = forwardEmailMsg( destinationAddress, smtpServer, fwdTag, pathFileName); if(okToDelete){ msgToDelete.add(pathFileName); }//end if }//end for loop on directory length //Sound an audio alert 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(); System.exit(0); }//end catch }//end forwardMsgs //===========================================// void deleteMsgs(){ System.out.println( "nDelete Msgs from POP3 server."); //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(); System.exit(0); }//end catch //Process the files in the msgToDelete // collection and delete those messages from // the email pop3Server. 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); //Deletion of a message from the pop3Server // is accomplished by marking the message // for deletion while in the TRANSACTION // state. The message is actually deleted // when the client sends a QUIT command to // the pop3Server causing the pop3Server to // enter the UPDATE state. If the program // aborts prematurely before sending a QUIT // command, marked messages are not deleted // from the pop3Server. //Mark the message for deletion. /* //Disable the following statement if you // enable message deletion. System.out.println( "Message deletion disabled"); */ //Disable this code to disable deletion of // messages from the pop3Server outputStream.println("DELE " + msgNumber); //Validate the response and display // progress. Will terminate on DELE ERR. validateOneLine(); System.out.print(msgNumber + " "); //Disable to here to disable deletion of // messages from the pop3Server //You can customize the program to either // move the message files from the working // directory into an archive directory, or // to simply delete the message files from // the working directory without saving // them in an archive directory. //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() System.out.println();//blank line //Terminate the session with the pop3Server // causing the marked messages to actually be // deleted from the pop3Server. 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(); System.exit(0); }//end catch System.out.println("Messages deleted " + "from pop3Server."); }//end deleteMsgs //===========================================// }//end class BigDog06forward //=============================================// |
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.