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