Home | History | Annotate | Download | only in apt
      1 		--------------------------------------------------
      2 					StubFtpServer Getting Started
      3 		--------------------------------------------------
      4 
      5 StubFtpServer - Getting Started
      6 
      7   <<StubFtpServer>> is a "stub" implementation of an FTP server. It supports the main FTP commands by 
      8   implementing command handlers for each of the corresponding low-level FTP server commands (e.g. RETR, 
      9   DELE, LIST). These <CommandHandler>s can be individually configured to return custom data or reply codes, 
     10   allowing simulation of a complete range of both success and failure scenarios. The <CommandHandler>s can 
     11   also be interrogated to verify command invocation data such as command parameters and timestamps.
     12 
     13   <<StubFtpServer>> works out of the box with reasonable defaults, but can be fully configured 
     14   programmatically or within a {{{http://www.springframework.org/}Spring Framework}} (or similar) container.
     15 
     16   Here is how to start the <<StubFtpServer>> with the default configuration. This will return 
     17   success reply codes, and return empty data (for retrieved files, directory listings, etc.).
     18 
     19 +------------------------------------------------------------------------------  
     20 StubFtpServer stubFtpServer = new StubFtpServer();
     21 stubFtpServer.start();
     22 +------------------------------------------------------------------------------  
     23   
     24   If you are running on a Unix system, you probably need to use a different server control port, since
     25   the default port (21) is likely already in use or cannot be bound from a user process. Use the
     26   <<<StubFtpServer.setServerControlPort(int serverControlPort)>>> method to set a different port
     27   number, such as 9187. 
     28   
     29 * CommandHandlers
     30 
     31   <CommandHandler>s are the heart of the <<StubFtpServer>>.
     32 
     33   <<StubFtpServer>> creates an appropriate default <CommandHandler> for each (supported) FTP server 
     34   command. See the list of <CommandHandler> classes associated with FTP server commands in 
     35   {{{stubftpserver-commandhandlers.html}FTP Commands and CommandHandlers}}.
     36 
     37   You can retrieve the existing <CommandHandler> defined for an FTP server command by calling the
     38   <<<StubFtpServer.getCommandHandler(String name)>>> method, passing in the FTP server command
     39   name. For example:
     40   
     41 +------------------------------------------------------------------------------  
     42 PwdCommandHandler pwdCommandHandler = (PwdCommandHandler) stubFtpServer.getCommandHandler("PWD");
     43 +------------------------------------------------------------------------------  
     44 
     45   You can replace the existing <CommandHandler> defined for an FTP server command by calling the
     46   <<<StubFtpServer.setCommandHandler(String name, CommandHandler commandHandler)>>> method, passing 
     47   in the FTP server command name, such as <<<"STOR">>> or <<<"USER">>>, and the 
     48   <<<CommandHandler>>> instance. For example:
     49   
     50 +------------------------------------------------------------------------------  
     51 PwdCommandHandler pwdCommandHandler = new PwdCommandHandler();
     52 pwdCommandHandler.setDirectory("some/dir");
     53 stubFtpServer.setCommandHandler("PWD", pwdCommandHandler);
     54 +------------------------------------------------------------------------------  
     55 
     56 
     57 ** Generic CommandHandlers
     58 
     59   <<StubFtpServer>> includes a couple generic <CommandHandler> classes that can be used to replace
     60   the default command handler for an FTP command. See the Javadoc for more information.
     61   
     62   * <<StaticReplyCommadHandler>>
     63 
     64     <<<StaticReplyCommadHandler>>> is a <CommandHandler> that always sends back the configured reply 
     65     code and text. This can be a useful replacement for a default <CommandHandler> if you want a 
     66     certain FTP command to always send back an error reply code.
     67   
     68   * <<SimpleCompositeCommandHandler>>
     69 
     70     <<<SimpleCompositeCommandHandler>>> is a composite <CommandHandler> that manages an internal 
     71     ordered list of <CommandHandler>s to which it delegates. Starting with the first 
     72     <CommandHandler> in the list, each invocation of this composite handler will invoke (delegate to) 
     73     the current internal <CommandHander>. Then it moves on the next <CommandHandler> in the internal list.
     74 
     75 
     76 * Programmatic Configuration
     77 
     78   You can customize the behavior of the FTP server through programmatic configuration.
     79   <<StubFtpServer>> automatically creates a default <CommandHandler> for each supported command.
     80   If you want to customize the behavior of the server, you should create and configure a replacement
     81   <CommandHandler> for each command to be customized.
     82   
     83   The {{{#Example}Example Test Using Stub Ftp Server}} illustrates replacing the default 
     84   <CommandHandler> with a customized handler.
     85 
     86 * Spring Configuration
     87 
     88   You can easily configure a <<StubFtpServer>> instance in the 
     89   {{{http://www.springframework.org/}Spring Framework}}. The following example shows a <Spring>
     90   configuration file.
     91   
     92 +------------------------------------------------------------------------------  
     93 <?xml version="1.0" encoding="UTF-8"?>
     94 
     95 <beans xmlns="http://www.springframework.org/schema/beans"
     96        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     97        xsi:schemaLocation="http://www.springframework.org/schema/beans 
     98            http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
     99 
    100   <bean id="stubFtpServer" class="org.mockftpserver.stub.StubFtpServer">
    101   
    102     <property name="commandHandlers">
    103       <map>
    104         <entry key="LIST">
    105           <bean class="org.mockftpserver.stub.command.ListCommandHandler">
    106             <property name="directoryListing">
    107               <value>
    108                 11-09-01 12:30PM  406348 File2350.log
    109                 11-01-01 1:30PM &lt;DIR&gt; 0 archive
    110               </value>
    111             </property>
    112           </bean>
    113         </entry>
    114 
    115         <entry key="PWD">
    116           <bean class="org.mockftpserver.stub.command.PwdCommandHandler">
    117             <property name="directory" value="foo/bar" />
    118           </bean>
    119         </entry>
    120 
    121         <entry key="DELE">
    122           <bean class="org.mockftpserver.stub.command.DeleCommandHandler">
    123             <property name="replyCode" value="450" />
    124           </bean>
    125         </entry>
    126 
    127         <entry key="RETR">
    128           <bean class="org.mockftpserver.stub.command.RetrCommandHandler">
    129             <property name="fileContents" 
    130               value="Sample file contents line 1&#10;Line 2&#10;Line 3"/>
    131           </bean>
    132         </entry>
    133 
    134       </map>
    135     </property>
    136   </bean>
    137 
    138 </beans>
    139 +------------------------------------------------------------------------------  
    140 
    141   This example overrides the default handlers for the following FTP commands:
    142   
    143   * LIST - replies with a predefined directory listing
    144   
    145   * PWD - replies with a predefined directory pathname
    146   
    147   * DELE - replies with an error reply code (450)
    148   
    149   * RETR - replies with predefined contents for a retrieved file
    150 
    151   []
    152 
    153   And here is the Java code to load the above <Spring> configuration file and start the
    154   configured <<StubFtpServer>>.
    155 
    156 +------------------------------------------------------------------------------  
    157 ApplicationContext context = new ClassPathXmlApplicationContext("stubftpserver-beans.xml");
    158 stubFtpServer = (StubFtpServer) context.getBean("stubFtpServer");
    159 stubFtpServer.start();
    160 +------------------------------------------------------------------------------  
    161 
    162 
    163 * Retrieving Command Invocation Data
    164 
    165   Each <CommandHandler> manages a List of <<<InvocationRecord>>> objects -- one for each time the
    166   <CommandHandler> is invoked. An <<<InvocationRecord>>> contains the <<<Command>>> that triggered
    167   the invocation (containing the command name and parameters), as well as the invocation timestamp
    168   and client host address. The <<<InvocationRecord>>> also contains a <<<Map>>>, with optional 
    169   <CommandHandler>-specific data. See the Javadoc for more information.
    170   
    171   You can retrieve the <<<InvocationRecord>>> from a <CommandHandler> by calling the
    172   <<<getInvocation(int index)>>> method, passing in the (zero-based) index of the desired
    173   invocation. You can get the number of invocations for a <CommandHandler> by calling
    174   <<<numberOfInvocations()>>>. The {{{#Example}Example Test Using Stub Ftp Server}} below illustrates 
    175   retrieving and interrogating an <<<InvocationRecord>>> from a <CommandHandler>.
    176   
    177   
    178   
    179 * {Example} Test Using StubFtpServer
    180 
    181   This section includes a simplified example of FTP client code to be tested, and a JUnit 
    182   test for it that uses <<StubFtpServer>>.
    183 
    184 ** FTP Client Code
    185 
    186   The following <<<RemoteFile>>> class includes a <<<readFile()>>> method that retrieves a remote 
    187   ASCII file and returns its contents as a String. This class uses the <<<FTPClient>>> from the
    188   {{{http://commons.apache.org/net/}Apache Commons Net}} framework.
    189 
    190 +------------------------------------------------------------------------------  
    191 public class RemoteFile {
    192 
    193     private String server;
    194 
    195     public String readFile(String filename) throws SocketException, IOException {
    196 
    197         FTPClient ftpClient = new FTPClient();
    198         ftpClient.connect(server);
    199 
    200         ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
    201         boolean success = ftpClient.retrieveFile(filename, outputStream);
    202         ftpClient.disconnect();
    203 
    204         if (!success) {
    205             throw new IOException("Retrieve file failed: " + filename);
    206         }
    207         return outputStream.toString();
    208     }
    209     
    210     public void setServer(String server) {
    211         this.server = server;
    212     }
    213     
    214     // Other methods ...
    215 }
    216 +------------------------------------------------------------------------------  
    217 
    218 ** JUnit Test For FTP Client Code Using StubFtpServer
    219 
    220   The following <<<RemoteFileTest>>> class includes a couple of JUnit tests that use 
    221   <<StubFtpServer>>.
    222 
    223 +------------------------------------------------------------------------------  
    224 import java.io.IOException;
    225 import org.mockftpserver.core.command.InvocationRecord;
    226 import org.mockftpserver.stub.StubFtpServer;
    227 import org.mockftpserver.stub.command.RetrCommandHandler;
    228 import org.mockftpserver.test.AbstractTest;
    229 
    230 public class RemoteFileTest extends AbstractTest {
    231 
    232     private static final String FILENAME = "dir/sample.txt";
    233 
    234     private RemoteFile remoteFile;
    235     private StubFtpServer stubFtpServer;
    236     
    237     /**
    238      * Test readFile() method 
    239      */
    240     public void testReadFile() throws Exception {
    241 
    242         final String CONTENTS = "abcdef 1234567890";
    243 
    244         // Replace the default RETR CommandHandler; customize returned file contents
    245         RetrCommandHandler retrCommandHandler = new RetrCommandHandler();
    246         retrCommandHandler.setFileContents(CONTENTS);
    247         stubFtpServer.setCommandHandler("RETR", retrCommandHandler);
    248         
    249         stubFtpServer.start();
    250         
    251         String contents = remoteFile.readFile(FILENAME);
    252 
    253         // Verify returned file contents
    254         assertEquals("contents", CONTENTS, contents);
    255         
    256         // Verify the submitted filename
    257         InvocationRecord invocationRecord = retrCommandHandler.getInvocation(0);
    258         String filename = invocationRecord.getString(RetrCommandHandler.PATHNAME_KEY);
    259         assertEquals("filename", FILENAME, filename);
    260     }
    261 
    262     /**
    263      * Test the readFile() method when the FTP transfer fails (returns a non-success reply code) 
    264      */
    265     public void testReadFileThrowsException() {
    266 
    267         // Replace the default RETR CommandHandler; return failure reply code
    268         RetrCommandHandler retrCommandHandler = new RetrCommandHandler();
    269         retrCommandHandler.setFinalReplyCode(550);
    270         stubFtpServer.setCommandHandler("RETR", retrCommandHandler);
    271         
    272         stubFtpServer.start();
    273 
    274         try {
    275             remoteFile.readFile(FILENAME);
    276             fail("Expected IOException");
    277         }
    278         catch (IOException expected) {
    279             // Expected this
    280         }
    281     }
    282     
    283     /**
    284      * @see org.mockftpserver.test.AbstractTest#setUp()
    285      */
    286     protected void setUp() throws Exception {
    287         super.setUp();
    288         remoteFile = new RemoteFile();
    289         remoteFile.setServer("localhost");
    290         stubFtpServer = new StubFtpServer();
    291     }
    292 
    293     /**
    294      * @see org.mockftpserver.test.AbstractTest#tearDown()
    295      */
    296     protected void tearDown() throws Exception {
    297         super.tearDown();
    298         stubFtpServer.stop();
    299     }
    300 }
    301 +------------------------------------------------------------------------------  
    302 
    303   Things to note about the above test:
    304   
    305   * The <<<StubFtpServer>>> instance is created in the <<<setUp()>>> method, but is not started
    306     there because it must be configured differently for each test. The <<<StubFtpServer>>> instance 
    307     is stopped in the <<<tearDown()>>> method, to ensure that it is stopped, even if the test fails.
    308   
    309 
    310 * FTP Command Reply Text ResourceBundle
    311 
    312   The default text asociated with each FTP command reply code is contained within the
    313   "ReplyText.properties" ResourceBundle file. You can customize these messages by providing a
    314   locale-specific ResourceBundle file on the CLASSPATH, according to the normal lookup rules of 
    315   the ResourceBundle class (e.g., "ReplyText_de.properties"). Alternatively, you can 
    316   completely replace the ResourceBundle file by calling the calling the 
    317   <<<StubFtpServer.setReplyTextBaseName(String)>>> method. 
    318