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