summaryrefslogtreecommitdiff
path: root/branches/1.x_Branch/src/site/apt/stubftpserver-getting-started.apt
blob: 19de64912870a679699255ef970a503d83f957a0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
		--------------------------------------------------
					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();
+------------------------------------------------------------------------------  
  
* 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.setOverrideFinalReplyCode(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.