--------------------------------------------------
					StubFtpServer Getting Started
		--------------------------------------------------

StubFtpServer - Getting Started

  <<StubFtpServer>> is a "stub" implementation of an FTP server. It supports the main FTP commands by 
  implementing command handlers for each of the corresponding low-level FTP server commands (e.g. RETR, 
  DELE, LIST). These <CommandHandler>s can be individually configured to return custom data or reply codes, 
  allowing simulation of a complete range of both success and failure scenarios. The <CommandHandler>s can 
  also be interrogated to verify command invocation data such as command parameters and timestamps.

  <<StubFtpServer>> works out of the box with reasonable defaults, but can be fully configured 
  programmatically or within a {{{http://www.springframework.org/}Spring Framework}} (or similar) container.

  Here is how to start the <<StubFtpServer>> with the default configuration. This will return 
  success reply codes, and return empty data (for retrieved files, directory listings, etc.).

+------------------------------------------------------------------------------  
StubFtpServer stubFtpServer = new StubFtpServer();
stubFtpServer.start();
+------------------------------------------------------------------------------  
  
  If you are running on a Unix system, you probably need to use a different server control port, since
  the default port (21) is likely already in use or cannot be bound from a user process. Use the
  <<<StubFtpServer.setServerControlPort(int serverControlPort)>>> method to set a different port
  number, such as 9187. 
  
* CommandHandlers

  <CommandHandler>s are the heart of the <<StubFtpServer>>.

  <<StubFtpServer>> creates an appropriate default <CommandHandler> for each (supported) FTP server 
  command. See the list of <CommandHandler> classes associated with FTP server commands in 
  {{{stubftpserver-commandhandlers.html}FTP Commands and CommandHandlers}}.

  You can retrieve the existing <CommandHandler> defined for an FTP server command by calling the
  <<<StubFtpServer.getCommandHandler(String name)>>> method, passing in the FTP server command
  name. For example:
  
+------------------------------------------------------------------------------  
PwdCommandHandler pwdCommandHandler = (PwdCommandHandler) stubFtpServer.getCommandHandler("PWD");
+------------------------------------------------------------------------------  

  You can replace the existing <CommandHandler> defined for an FTP server command by calling the
  <<<StubFtpServer.setCommandHandler(String name, CommandHandler commandHandler)>>> method, passing 
  in the FTP server command name, such as <<<"STOR">>> or <<<"USER">>>, and the 
  <<<CommandHandler>>> instance. For example:
  
+------------------------------------------------------------------------------  
PwdCommandHandler pwdCommandHandler = new PwdCommandHandler();
pwdCommandHandler.setDirectory("some/dir");
stubFtpServer.setCommandHandler("PWD", pwdCommandHandler);
+------------------------------------------------------------------------------  


** Generic CommandHandlers

  <<StubFtpServer>> includes a couple generic <CommandHandler> classes that can be used to replace
  the default command handler for an FTP command. See the Javadoc for more information.
  
  * <<StaticReplyCommadHandler>>

    <<<StaticReplyCommadHandler>>> is a <CommandHandler> that always sends back the configured reply 
    code and text. This can be a useful replacement for a default <CommandHandler> if you want a 
    certain FTP command to always send back an error reply code.
  
  * <<SimpleCompositeCommandHandler>>

    <<<SimpleCompositeCommandHandler>>> is a composite <CommandHandler> that manages an internal 
    ordered list of <CommandHandler>s to which it delegates. Starting with the first 
    <CommandHandler> in the list, each invocation of this composite handler will invoke (delegate to) 
    the current internal <CommandHander>. Then it moves on the next <CommandHandler> in the internal list.


* Programmatic Configuration

  You can customize the behavior of the FTP server through programmatic configuration.
  <<StubFtpServer>> automatically creates a default <CommandHandler> for each supported command.
  If you want to customize the behavior of the server, you should create and configure a replacement
  <CommandHandler> for each command to be customized.
  
  The {{{#Example}Example Test Using Stub Ftp Server}} illustrates replacing the default 
  <CommandHandler> with a customized handler.

* Spring Configuration

  You can easily configure a <<StubFtpServer>> instance in the 
  {{{http://www.springframework.org/}Spring Framework}}. The following example shows a <Spring>
  configuration file.
  
+------------------------------------------------------------------------------  
<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans 
           http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">

  <bean id="stubFtpServer" class="org.mockftpserver.stub.StubFtpServer">
  
    <property name="commandHandlers">
      <map>
        <entry key="LIST">
          <bean class="org.mockftpserver.stub.command.ListCommandHandler">
            <property name="directoryListing">
              <value>
                11-09-01 12:30PM  406348 File2350.log
                11-01-01 1:30PM &lt;DIR&gt; 0 archive
              </value>
            </property>
          </bean>
        </entry>

        <entry key="PWD">
          <bean class="org.mockftpserver.stub.command.PwdCommandHandler">
            <property name="directory" value="foo/bar" />
          </bean>
        </entry>

        <entry key="DELE">
          <bean class="org.mockftpserver.stub.command.DeleCommandHandler">
            <property name="replyCode" value="450" />
          </bean>
        </entry>

        <entry key="RETR">
          <bean class="org.mockftpserver.stub.command.RetrCommandHandler">
            <property name="fileContents" 
              value="Sample file contents line 1&#10;Line 2&#10;Line 3"/>
          </bean>
        </entry>

      </map>
    </property>
  </bean>

</beans>
+------------------------------------------------------------------------------  

  This example overrides the default handlers for the following FTP commands:
  
  * LIST - replies with a predefined directory listing
  
  * PWD - replies with a predefined directory pathname
  
  * DELE - replies with an error reply code (450)
  
  * RETR - replies with predefined contents for a retrieved file

  []

  And here is the Java code to load the above <Spring> configuration file and start the
  configured <<StubFtpServer>>.

+------------------------------------------------------------------------------  
ApplicationContext context = new ClassPathXmlApplicationContext("stubftpserver-beans.xml");
stubFtpServer = (StubFtpServer) context.getBean("stubFtpServer");
stubFtpServer.start();
+------------------------------------------------------------------------------  


* Retrieving Command Invocation Data

  Each <CommandHandler> manages a List of <<<InvocationRecord>>> objects -- one for each time the
  <CommandHandler> is invoked. An <<<InvocationRecord>>> contains the <<<Command>>> that triggered
  the invocation (containing the command name and parameters), as well as the invocation timestamp
  and client host address. The <<<InvocationRecord>>> also contains a <<<Map>>>, with optional 
  <CommandHandler>-specific data. See the Javadoc for more information.
  
  You can retrieve the <<<InvocationRecord>>> from a <CommandHandler> by calling the
  <<<getInvocation(int index)>>> method, passing in the (zero-based) index of the desired
  invocation. You can get the number of invocations for a <CommandHandler> by calling
  <<<numberOfInvocations()>>>. The {{{#Example}Example Test Using Stub Ftp Server}} below illustrates 
  retrieving and interrogating an <<<InvocationRecord>>> from a <CommandHandler>.
  
  
  
* {Example} Test Using StubFtpServer

  This section includes a simplified example of FTP client code to be tested, and a JUnit 
  test for it that uses <<StubFtpServer>>.

** FTP Client Code

  The following <<<RemoteFile>>> class includes a <<<readFile()>>> method that retrieves a remote 
  ASCII file and returns its contents as a String. This class uses the <<<FTPClient>>> from the
  {{{http://commons.apache.org/net/}Apache Commons Net}} framework.

+------------------------------------------------------------------------------  
public class RemoteFile {

    private String server;

    public String readFile(String filename) throws SocketException, IOException {

        FTPClient ftpClient = new FTPClient();
        ftpClient.connect(server);

        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        boolean success = ftpClient.retrieveFile(filename, outputStream);
        ftpClient.disconnect();

        if (!success) {
            throw new IOException("Retrieve file failed: " + filename);
        }
        return outputStream.toString();
    }
    
    public void setServer(String server) {
        this.server = server;
    }
    
    // Other methods ...
}
+------------------------------------------------------------------------------  

** JUnit Test For FTP Client Code Using StubFtpServer

  The following <<<RemoteFileTest>>> class includes a couple of JUnit tests that use 
  <<StubFtpServer>>.

+------------------------------------------------------------------------------  
import java.io.IOException;
import org.mockftpserver.core.command.InvocationRecord;
import org.mockftpserver.stub.StubFtpServer;
import org.mockftpserver.stub.command.RetrCommandHandler;
import org.mockftpserver.test.AbstractTest;

public class RemoteFileTest extends AbstractTest {

    private static final String FILENAME = "dir/sample.txt";

    private RemoteFile remoteFile;
    private StubFtpServer stubFtpServer;
    
    /**
     * Test readFile() method 
     */
    public void testReadFile() throws Exception {

        final String CONTENTS = "abcdef 1234567890";

        // Replace the default RETR CommandHandler; customize returned file contents
        RetrCommandHandler retrCommandHandler = new RetrCommandHandler();
        retrCommandHandler.setFileContents(CONTENTS);
        stubFtpServer.setCommandHandler("RETR", retrCommandHandler);
        
        stubFtpServer.start();
        
        String contents = remoteFile.readFile(FILENAME);

        // Verify returned file contents
        assertEquals("contents", CONTENTS, contents);
        
        // Verify the submitted filename
        InvocationRecord invocationRecord = retrCommandHandler.getInvocation(0);
        String filename = invocationRecord.getString(RetrCommandHandler.PATHNAME_KEY);
        assertEquals("filename", FILENAME, filename);
    }

    /**
     * Test the readFile() method when the FTP transfer fails (returns a non-success reply code) 
     */
    public void testReadFileThrowsException() {

        // Replace the default RETR CommandHandler; return failure reply code
        RetrCommandHandler retrCommandHandler = new RetrCommandHandler();
        retrCommandHandler.setFinalReplyCode(550);
        stubFtpServer.setCommandHandler("RETR", retrCommandHandler);
        
        stubFtpServer.start();

        try {
            remoteFile.readFile(FILENAME);
            fail("Expected IOException");
        }
        catch (IOException expected) {
            // Expected this
        }
    }
    
    /**
     * @see org.mockftpserver.test.AbstractTest#setUp()
     */
    protected void setUp() throws Exception {
        super.setUp();
        remoteFile = new RemoteFile();
        remoteFile.setServer("localhost");
        stubFtpServer = new StubFtpServer();
    }

    /**
     * @see org.mockftpserver.test.AbstractTest#tearDown()
     */
    protected void tearDown() throws Exception {
        super.tearDown();
        stubFtpServer.stop();
    }
}
+------------------------------------------------------------------------------  

  Things to note about the above test:
  
  * The <<<StubFtpServer>>> instance is created in the <<<setUp()>>> method, but is not started
    there because it must be configured differently for each test. The <<<StubFtpServer>>> instance 
    is stopped in the <<<tearDown()>>> method, to ensure that it is stopped, even if the test fails.
  

* FTP Command Reply Text ResourceBundle

  The default text asociated with each FTP command reply code is contained within the
  "ReplyText.properties" ResourceBundle file. You can customize these messages by providing a
  locale-specific ResourceBundle file on the CLASSPATH, according to the normal lookup rules of 
  the ResourceBundle class (e.g., "ReplyText_de.properties"). Alternatively, you can 
  completely replace the ResourceBundle file by calling the calling the 
  <<<StubFtpServer.setReplyTextBaseName(String)>>> method.