Introduction
Before writing this article, I wrote one on the MIMER
, a MIME Compliant Parser, which described an implementation of the MIME RFC 2045 specification. This is the continuation and shows the MIMER
library in action, i.e. implemented in an RFC 1939 compliant POP3 client.
Background
This POP3 client should not raise any eyebrows; even so, one could take a closer look at the way in which it is implemented. The source uses well-known GOF design patterns such as the command and state patterns. In doing so, it might be used as a "real" world example of how these patterns could be used.
Using the Code
The client as such is rather lightweight, summing up to only around 400 rows of code, mostly due to the fact that the client leaves the RFC 1939 implementation to the command and state patterns.
The Command Pattern
"Encapsulate a request as an object, thereby letting you parameterize clients with different requests, queue or log requests, and support undoable operations." (Gamma, E., R. Helm, R. Johnson, and J. Vlissides. Design Patterns: Elements of Reusable Object-Oriented Software. Reading, MA: Addison-Wesley, 1995).
The most frequent use of this pattern, as stated above, is to "support undoable operations". This is due to the fact that the pattern turns each command into an individual object, making it possible to store it for later use. This was, however, not the motivator for me in implementing the command pattern in this case. I saw it as an excellent way to separate the POP-client from its RFC 1939 specification, making it possible to extend it in the scenario where the specification should change. In doing so, the pattern made it possible for me to also implement the log functionality.
public abstract class POPCommand
{
public POPCommand(Stream receiver, string arguments){…}
protected void WriteCommand(){…}
protected void ReadResponse(){…}
public abstract bool Execute();
}
public class NoopCommand:POPCommand
{
public NoopCommand(Stream receiver): base(receiver, string.Empty){ }
public override bool Execute()
{
try
{
m_Command = "NOOP" + Environment.NewLine;
WriteCommand();
ReadResponse();
}
catch (Exception ex)
{
throw new POPException("Could not issue NOOP command!", ex);
}
return m_Response.StartsWith(OK);
}
}
The State Pattern
"Allow an object to alter its behaviour when its internal state changes. The object will appear to change its class." - See this website.
The state pattern is a rather easy pattern structurally involving only two parties: the context and its states. It is, in a way, the design pattern implementation of a UML state chart. The abstract POPState
class is defined as follows:
public abstract class POPState
{
public POPState(){}
public abstract bool IssueCommand(RFC1939.POPCommand command,
POPClient context);
public abstract bool IsLegalCommand(RFC1939.POPCommand command);
public new abstract string ToString();
}
The IssueCommand
method is a delegation of the context's (POPClient
) IssueCommand
method. The IsLegalCommand
method shall be able to determine if a command is legal to execute within the current state.
class DisconnectedState:POPState
{
public override bool IsLegalCommand(Popper.RFC1939.POPCommand command)
{
if (command is RFC1939.GreetingCommand)
return true;
return false;
}
The states are partial replicates of the context. If the context has a method named Method()
, it is the states which implement Method()
's different behaviour during the lifespan of the context. To accomplish this, the states must be allowed to change the state within which the context currently is. The state is allowed to pass the control over to another state, i.e. the context leaves the control of its behaviour over to the states.
public class POPClient:MIMER.IEndCriteriaStrategy
{
public bool IssueCommand(RFC1939.POPCommand command)
{
try
{
if (m_CurrentState.IssueCommand(command, this))
{
return true;
}
}...
class DisconnectedState:POPState
{
public override bool IssueCommand(Popper.RFC1939.POPCommand command,
POPClient context)
{
if (IsLegalCommand(command))
{
if (command.Execute())
{
context.LoggCommand(command);
context.CurrentState = AuthorizationState.GetInstance();
return true;
}
}
return false;
}
When looking at the diagram above, one might notice that the SetState
method is defined as public
. This is not the usual design; the SetState
method should only be visible within the same assembly (I could not find the internal modifier in Visio). This is to ensure that the client code could not change the state of the context from outside.
public POPState CurrentState
{
get
{
return m_CurrentState;
}
internal set
{
m_CurrentState = value;
OnStateChanged(this, new StateChangedEventArgs(m_CurrentState));
}
}
The Client
The POP3 client, as such, has all the basic functions needed to retrieve and manipulate mails from a POP3 compliant server. The client also implements the basic functionality for asynchronous message retrieval; see the BeginFetchMail()
and EndFetchMail()
methods. See this website for more comprehensive documentation.
Finally
This was my attempt to write a POP3 compliant client. It still needs some refinement; however, the basics are there. It should not be too hard for you to modify it to fit your needs. I will continuously improve the source and you will find it here.
By the way, when building the source, be certain to build the MIME
project before the Popper
project since the Popper
project depends on the MIMER
project.
Additions
Fellow coder sergeyv2002 requested a usage example, I have now added a test application project (PopConsoleTest
) available for download at the top of this article. Despite its name, it is not a Console application but a Windows application. The application is rather basic but should guide you somewhat on how to use the code library. I hope this helps.
History
- 2007-07-28: Article created
- 2007-11-15: Zip file & article updated
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.