xref: /aosp_15_r20/external/mockftpserver/MockFtpServer/src/site/apt/fakeftpserver-getting-started.apt (revision 876b9d758e92bb9ac60d09763a5f23a1c6322c5f)
1*876b9d75SXin Li		--------------------------------------------------
2*876b9d75SXin Li					FakeFtpServer Getting Started
3*876b9d75SXin Li		--------------------------------------------------
4*876b9d75SXin Li
5*876b9d75SXin LiFakeFtpServer - Getting Started
6*876b9d75SXin Li~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7*876b9d75SXin Li
8*876b9d75SXin Li  <<FakeFtpServer>> is a "fake" implementation of an FTP server. It provides a high-level abstraction for
9*876b9d75SXin Li  an FTP Server and is suitable for most testing and simulation scenarios. You define a virtual filesystem
10*876b9d75SXin Li  (internal, in-memory) containing an arbitrary set of files and directories. These files and directories can
11*876b9d75SXin Li  (optionally) have associated access permissions. You also configure a set of one or more user accounts that
12*876b9d75SXin Li  control which users can login to the FTP server, and their home (default) directories. The user account is
13*876b9d75SXin Li  also used when assigning file and directory ownership for new files.
14*876b9d75SXin Li
15*876b9d75SXin Li  <<FakeFtpServer>> processes FTP client requests and responds with reply codes and reply messages
16*876b9d75SXin Li  consistent with its configured file system and user accounts, including file and directory permissions,
17*876b9d75SXin Li  if they have been configured.
18*876b9d75SXin Li
19*876b9d75SXin Li  See the {{{./fakeftpserver-features.html}FakeFtpServer Features and Limitations}} page for more information on
20*876b9d75SXin Li  which features and scenarios are supported.
21*876b9d75SXin Li
22*876b9d75SXin Li  In general the steps for setting up and starting the <<<FakeFtpServer>>> are:
23*876b9d75SXin Li
24*876b9d75SXin Li  * Create a new <<<FakeFtpServer>>> instance, and optionally set the server control port (use a value of 0
25*876b9d75SXin Li    to automatically choose a free port number).
26*876b9d75SXin Li
27*876b9d75SXin Li  * Create and configure a <<<FileSystem>>>, and attach to the <<<FakeFtpServer>>> instance.
28*876b9d75SXin Li
29*876b9d75SXin Li  * Create and configure one or more <<<UserAccount>>> objects and attach to the <<<FakeFtpServer>>> instance.
30*876b9d75SXin Li
31*876b9d75SXin Li  []
32*876b9d75SXin Li
33*876b9d75SXin Li  Here is an example showing configuration and starting of an <<FakeFtpServer>> with a single user
34*876b9d75SXin Li  account and a (simulated) Windows file system, defining a directory containing two files.
35*876b9d75SXin Li
36*876b9d75SXin Li+------------------------------------------------------------------------------
37*876b9d75SXin LiFakeFtpServer fakeFtpServer = new FakeFtpServer();
38*876b9d75SXin LifakeFtpServer.addUserAccount(new UserAccount("user", "password", "c:\\data"));
39*876b9d75SXin Li
40*876b9d75SXin LiFileSystem fileSystem = new WindowsFakeFileSystem();
41*876b9d75SXin LifileSystem.add(new DirectoryEntry("c:\\data"));
42*876b9d75SXin LifileSystem.add(new FileEntry("c:\\data\\file1.txt", "abcdef 1234567890"));
43*876b9d75SXin LifileSystem.add(new FileEntry("c:\\data\\run.exe"));
44*876b9d75SXin LifakeFtpServer.setFileSystem(fileSystem);
45*876b9d75SXin Li
46*876b9d75SXin LifakeFtpServer.start();
47*876b9d75SXin Li+------------------------------------------------------------------------------
48*876b9d75SXin Li
49*876b9d75SXin Li  If you are running on a system where the default port (21) is already in use or cannot be bound
50*876b9d75SXin Li  from a user process (such as Unix), you probably need to use a different server control port. Use the
51*876b9d75SXin Li  <<<FakeFtpServer.setServerControlPort(int serverControlPort)>>> method to use a different port
52*876b9d75SXin Li  number. If you specify a value of <<<0>>>, then the server will use a free port number. Then call
53*876b9d75SXin Li  <<<getServerControlPort()>>> AFTER calling <<<start()>>> has been called to determine the actual port
54*876b9d75SXin Li  number being used. Or, you can pass in a specific port number, such as 9187.
55*876b9d75SXin Li
56*876b9d75SXin Li  <<FakeFtpServer>>  can be fully configured programmatically or within the
57*876b9d75SXin Li  {{{http://www.springframework.org/}Spring Framework}} or other dependency-injection container.
58*876b9d75SXin Li  The {{{#Example}Example Test Using FakeFtpServer}} below illustrates programmatic configuration of
59*876b9d75SXin Li  <<<FakeFtpServer>>>. Alternatively, the {{{#Spring}Configuration}} section later on illustrates how to use
60*876b9d75SXin Li  the <Spring Framework> to configure a <<<FakeFtpServer>>> instance.
61*876b9d75SXin Li
62*876b9d75SXin Li* {Example} Test Using FakeFtpServer
63*876b9d75SXin Li~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
64*876b9d75SXin Li
65*876b9d75SXin Li  This section includes a simplified example of FTP client code to be tested, and a JUnit
66*876b9d75SXin Li  test for it that programmatically configures and uses <<FakeFtpServer>>.
67*876b9d75SXin Li
68*876b9d75SXin Li** FTP Client Code
69*876b9d75SXin Li~~~~~~~~~~~~~~~~~~
70*876b9d75SXin Li
71*876b9d75SXin Li  The following <<<RemoteFile>>> class includes a <<<readFile()>>> method that retrieves a remote
72*876b9d75SXin Li  ASCII file and returns its contents as a String. This class uses the <<<FTPClient>>> from the
73*876b9d75SXin Li  {{{http://commons.apache.org/net/}Apache Commons Net}} framework.
74*876b9d75SXin Li
75*876b9d75SXin Li+------------------------------------------------------------------------------
76*876b9d75SXin Lipublic class RemoteFile {
77*876b9d75SXin Li
78*876b9d75SXin Li    public static final String USERNAME = "user";
79*876b9d75SXin Li    public static final String PASSWORD = "password";
80*876b9d75SXin Li
81*876b9d75SXin Li    private String server;
82*876b9d75SXin Li    private int port;
83*876b9d75SXin Li
84*876b9d75SXin Li    public String readFile(String filename) throws IOException {
85*876b9d75SXin Li
86*876b9d75SXin Li        FTPClient ftpClient = new FTPClient();
87*876b9d75SXin Li        ftpClient.connect(server, port);
88*876b9d75SXin Li        ftpClient.login(USERNAME, PASSWORD);
89*876b9d75SXin Li
90*876b9d75SXin Li        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
91*876b9d75SXin Li        boolean success = ftpClient.retrieveFile(filename, outputStream);
92*876b9d75SXin Li        ftpClient.disconnect();
93*876b9d75SXin Li
94*876b9d75SXin Li        if (!success) {
95*876b9d75SXin Li            throw new IOException("Retrieve file failed: " + filename);
96*876b9d75SXin Li        }
97*876b9d75SXin Li        return outputStream.toString();
98*876b9d75SXin Li    }
99*876b9d75SXin Li
100*876b9d75SXin Li    public void setServer(String server) {
101*876b9d75SXin Li        this.server = server;
102*876b9d75SXin Li    }
103*876b9d75SXin Li
104*876b9d75SXin Li    public void setPort(int port) {
105*876b9d75SXin Li        this.port = port;
106*876b9d75SXin Li    }
107*876b9d75SXin Li
108*876b9d75SXin Li    // Other methods ...
109*876b9d75SXin Li}
110*876b9d75SXin Li+------------------------------------------------------------------------------
111*876b9d75SXin Li
112*876b9d75SXin Li** JUnit Test For FTP Client Code Using FakeFtpServer
113*876b9d75SXin Li~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
114*876b9d75SXin Li
115*876b9d75SXin Li  The following <<<RemoteFileTest>>> class includes a couple of JUnit tests that use
116*876b9d75SXin Li  <<FakeFtpServer>>.
117*876b9d75SXin Li
118*876b9d75SXin Li+------------------------------------------------------------------------------
119*876b9d75SXin Liimport org.mockftpserver.fake.filesystem.FileEntry;
120*876b9d75SXin Liimport org.mockftpserver.fake.filesystem.FileSystem;
121*876b9d75SXin Liimport org.mockftpserver.fake.filesystem.UnixFakeFileSystem;
122*876b9d75SXin Liimport org.mockftpserver.fake.FakeFtpServer;
123*876b9d75SXin Liimport org.mockftpserver.fake.UserAccount;
124*876b9d75SXin Liimport org.mockftpserver.stub.example.RemoteFile;
125*876b9d75SXin Liimport org.mockftpserver.test.AbstractTest;
126*876b9d75SXin Liimport java.io.IOException;
127*876b9d75SXin Liimport java.util.List;
128*876b9d75SXin Li
129*876b9d75SXin Lipublic class RemoteFileTest extends AbstractTest {
130*876b9d75SXin Li
131*876b9d75SXin Li    private static final String HOME_DIR = "/";
132*876b9d75SXin Li    private static final String FILE = "/dir/sample.txt";
133*876b9d75SXin Li    private static final String CONTENTS = "abcdef 1234567890";
134*876b9d75SXin Li
135*876b9d75SXin Li    private RemoteFile remoteFile;
136*876b9d75SXin Li    private FakeFtpServer fakeFtpServer;
137*876b9d75SXin Li
138*876b9d75SXin Li    public void testReadFile() throws Exception {
139*876b9d75SXin Li        String contents = remoteFile.readFile(FILE);
140*876b9d75SXin Li        assertEquals("contents", CONTENTS, contents);
141*876b9d75SXin Li    }
142*876b9d75SXin Li
143*876b9d75SXin Li    public void testReadFileThrowsException() {
144*876b9d75SXin Li        try {
145*876b9d75SXin Li            remoteFile.readFile("NoSuchFile.txt");
146*876b9d75SXin Li            fail("Expected IOException");
147*876b9d75SXin Li        }
148*876b9d75SXin Li        catch (IOException expected) {
149*876b9d75SXin Li            // Expected this
150*876b9d75SXin Li        }
151*876b9d75SXin Li    }
152*876b9d75SXin Li
153*876b9d75SXin Li    protected void setUp() throws Exception {
154*876b9d75SXin Li        super.setUp();
155*876b9d75SXin Li        fakeFtpServer = new FakeFtpServer();
156*876b9d75SXin Li        fakeFtpServer.setServerControlPort(0);  // use any free port
157*876b9d75SXin Li
158*876b9d75SXin Li        FileSystem fileSystem = new UnixFakeFileSystem();
159*876b9d75SXin Li        fileSystem.add(new FileEntry(FILE, CONTENTS));
160*876b9d75SXin Li        fakeFtpServer.setFileSystem(fileSystem);
161*876b9d75SXin Li
162*876b9d75SXin Li        UserAccount userAccount = new UserAccount(RemoteFile.USERNAME, RemoteFile.PASSWORD, HOME_DIR);
163*876b9d75SXin Li        fakeFtpServer.addUserAccount(userAccount);
164*876b9d75SXin Li
165*876b9d75SXin Li        fakeFtpServer.start();
166*876b9d75SXin Li        int port = fakeFtpServer.getServerControlPort();
167*876b9d75SXin Li
168*876b9d75SXin Li        remoteFile = new RemoteFile();
169*876b9d75SXin Li        remoteFile.setServer("localhost");
170*876b9d75SXin Li        remoteFile.setPort(port);
171*876b9d75SXin Li    }
172*876b9d75SXin Li
173*876b9d75SXin Li    protected void tearDown() throws Exception {
174*876b9d75SXin Li        super.tearDown();
175*876b9d75SXin Li        fakeFtpServer.stop();
176*876b9d75SXin Li    }
177*876b9d75SXin Li}
178*876b9d75SXin Li+------------------------------------------------------------------------------
179*876b9d75SXin Li
180*876b9d75SXin Li  Things to note about the above test:
181*876b9d75SXin Li
182*876b9d75SXin Li  * The <<<FakeFtpServer>>> instance is created and started in the <<<setUp()>>> method and
183*876b9d75SXin Li    stopped in the <<<tearDown()>>> method, to ensure that it is stopped, even if the test fails.
184*876b9d75SXin Li
185*876b9d75SXin Li  * The server control port is set to 0 using <<<fakeFtpServer.setServerControlPort(PORT)>>>.
186*876b9d75SXin Li    This means it will dynamically choose a free port. This is necessary if you are running on a
187*876b9d75SXin Li    system where the default port (21) is already in use or cannot be bound from a user process (such as Unix).
188*876b9d75SXin Li
189*876b9d75SXin Li  * The <<<UnixFakeFileSystem>>> filesystem is configured and attached to the <<<FakeFtpServer>>> instance
190*876b9d75SXin Li    in the <<<setUp()>>> method. That includes creating a predefined <<<"/dir/sample.txt">>> file with the
191*876b9d75SXin Li    specified file contents. The <<<UnixFakeFileSystem>>> has a <<<createParentDirectoriesAutomatically>>>
192*876b9d75SXin Li    attribute, which defaults to <<<true>>>, meaning that parent directories will be created automatically,
193*876b9d75SXin Li    as necessary. In this case, that means that the <<<"/">>> and <<<"/dir">>> parent directories will be created,
194*876b9d75SXin Li    even though not explicitly specified.
195*876b9d75SXin Li
196*876b9d75SXin Li  * A single <<<UserAccount>>> with the specified username, password and home directory is configured and
197*876b9d75SXin Li    attached to the <<<FakeFtpServer>>> instance in the <<<setUp()>>> method. That configured user ("user")
198*876b9d75SXin Li    is the only one that will be able to sucessfully log in to the <<<FakeFtpServer>>>.
199*876b9d75SXin Li
200*876b9d75SXin Li
201*876b9d75SXin Li* {Spring} Configuration
202*876b9d75SXin Li~~~~~~~~~~~~~~~~~~~~~~~~
203*876b9d75SXin Li
204*876b9d75SXin Li  You can easily configure a <<<FakeFtpServer>>> instance in the
205*876b9d75SXin Li  {{{http://www.springframework.org/}Spring Framework}} or another, similar dependency-injection container.
206*876b9d75SXin Li
207*876b9d75SXin Li** Simple Spring Configuration Example
208*876b9d75SXin Li~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
209*876b9d75SXin Li
210*876b9d75SXin Li  The following example shows a <Spring> configuration file for a simple <<<FakeFtpServer>>> instance.
211*876b9d75SXin Li
212*876b9d75SXin Li+------------------------------------------------------------------------------
213*876b9d75SXin Li<?xml version="1.0" encoding="UTF-8"?>
214*876b9d75SXin Li
215*876b9d75SXin Li<beans xmlns="http://www.springframework.org/schema/beans"
216*876b9d75SXin Li       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
217*876b9d75SXin Li       xsi:schemaLocation="http://www.springframework.org/schema/beans
218*876b9d75SXin Li       		http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
219*876b9d75SXin Li
220*876b9d75SXin Li    <bean id="fakeFtpServer" class="org.mockftpserver.fake.FakeFtpServer">
221*876b9d75SXin Li        <property name="serverControlPort" value="9981"/>
222*876b9d75SXin Li        <property name="systemName" value="UNIX"/>
223*876b9d75SXin Li        <property name="userAccounts">
224*876b9d75SXin Li            <list>
225*876b9d75SXin Li                <bean class="org.mockftpserver.fake.UserAccount">
226*876b9d75SXin Li                    <property name="username" value="joe"/>
227*876b9d75SXin Li                    <property name="password" value="password"/>
228*876b9d75SXin Li                    <property name="homeDirectory" value="/"/>
229*876b9d75SXin Li                </bean>
230*876b9d75SXin Li            </list>
231*876b9d75SXin Li        </property>
232*876b9d75SXin Li
233*876b9d75SXin Li        <property name="fileSystem">
234*876b9d75SXin Li            <bean class="org.mockftpserver.fake.filesystem.UnixFakeFileSystem">
235*876b9d75SXin Li                <property name="createParentDirectoriesAutomatically" value="false"/>
236*876b9d75SXin Li                <property name="entries">
237*876b9d75SXin Li                    <list>
238*876b9d75SXin Li                        <bean class="org.mockftpserver.fake.filesystem.DirectoryEntry">
239*876b9d75SXin Li                            <property name="path" value="/"/>
240*876b9d75SXin Li                        </bean>
241*876b9d75SXin Li                        <bean class="org.mockftpserver.fake.filesystem.FileEntry">
242*876b9d75SXin Li                            <property name="path" value="/File.txt"/>
243*876b9d75SXin Li                            <property name="contents" value="abcdefghijklmnopqrstuvwxyz"/>
244*876b9d75SXin Li                        </bean>
245*876b9d75SXin Li                    </list>
246*876b9d75SXin Li                </property>
247*876b9d75SXin Li            </bean>
248*876b9d75SXin Li        </property>
249*876b9d75SXin Li
250*876b9d75SXin Li    </bean>
251*876b9d75SXin Li
252*876b9d75SXin Li</beans>
253*876b9d75SXin Li+------------------------------------------------------------------------------
254*876b9d75SXin Li
255*876b9d75SXin Li  Things to note about the above example:
256*876b9d75SXin Li
257*876b9d75SXin Li  * The <<<FakeFtpServer>>> instance has a single user account for username "joe", password "password"
258*876b9d75SXin Li    and home (default) directory of "/".
259*876b9d75SXin Li
260*876b9d75SXin Li  * A <<<UnixFakeFileSystem>>> instance is configured with a predefined directory of "/" and a
261*876b9d75SXin Li    "/File.txt" file with the specified contents.
262*876b9d75SXin Li
263*876b9d75SXin Li  []
264*876b9d75SXin Li
265*876b9d75SXin Li  And here is the Java code to load the above <Spring> configuration file and start the
266*876b9d75SXin Li  configured <<FakeFtpServer>>.
267*876b9d75SXin Li
268*876b9d75SXin Li+------------------------------------------------------------------------------
269*876b9d75SXin LiApplicationContext context = new ClassPathXmlApplicationContext("fakeftpserver-beans.xml");
270*876b9d75SXin LiFakeFtpServer = (FakeFtpServer) context.getBean("FakeFtpServer");
271*876b9d75SXin LiFakeFtpServer.start();
272*876b9d75SXin Li+------------------------------------------------------------------------------
273*876b9d75SXin Li
274*876b9d75SXin Li
275*876b9d75SXin Li** Spring Configuration Example With File and Directory Permissions
276*876b9d75SXin Li~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
277*876b9d75SXin Li
278*876b9d75SXin Li  The following example shows a <Spring> configuration file for a <<<FakeFtpServer>>> instance that
279*876b9d75SXin Li  also configures file and directory permissions. This will enable the <<<FakeFtpServer>>> to reply
280*876b9d75SXin Li  with proper error codes when the logged in user does not have the required permissions to access
281*876b9d75SXin Li  directories or files.
282*876b9d75SXin Li
283*876b9d75SXin Li+------------------------------------------------------------------------------
284*876b9d75SXin Li<?xml version="1.0" encoding="UTF-8"?>
285*876b9d75SXin Li
286*876b9d75SXin Libeans xmlns="http://www.springframework.org/schema/beans"
287*876b9d75SXin Li       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
288*876b9d75SXin Li       xsi:schemaLocation="http://www.springframework.org/schema/beans
289*876b9d75SXin Li       		http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
290*876b9d75SXin Li
291*876b9d75SXin Li    <bean id="fakeFtpServer" class="org.mockftpserver.fake.FakeFtpServer">
292*876b9d75SXin Li        <property name="serverControlPort" value="9981"/>
293*876b9d75SXin Li        <property name="userAccounts">
294*876b9d75SXin Li            <list>
295*876b9d75SXin Li                <bean class="org.mockftpserver.fake.UserAccount">
296*876b9d75SXin Li                    <property name="username" value="joe"/>
297*876b9d75SXin Li                    <property name="password" value="password"/>
298*876b9d75SXin Li                    <property name="homeDirectory" value="c:\"/>
299*876b9d75SXin Li                </bean>
300*876b9d75SXin Li            </list>
301*876b9d75SXin Li        </property>
302*876b9d75SXin Li
303*876b9d75SXin Li        <property name="fileSystem">
304*876b9d75SXin Li            <bean class="org.mockftpserver.fake.filesystem.WindowsFakeFileSystem">
305*876b9d75SXin Li                <property name="createParentDirectoriesAutomatically" value="false"/>
306*876b9d75SXin Li                <property name="entries">
307*876b9d75SXin Li                    <list>
308*876b9d75SXin Li                        <bean class="org.mockftpserver.fake.filesystem.DirectoryEntry">
309*876b9d75SXin Li                            <property name="path" value="c:\"/>
310*876b9d75SXin Li                            <property name="permissionsFromString" value="rwxrwxrwx"/>
311*876b9d75SXin Li                            <property name="owner" value="joe"/>
312*876b9d75SXin Li                            <property name="group" value="users"/>
313*876b9d75SXin Li                        </bean>
314*876b9d75SXin Li                        <bean class="org.mockftpserver.fake.filesystem.FileEntry">
315*876b9d75SXin Li                            <property name="path" value="c:\File1.txt"/>
316*876b9d75SXin Li                            <property name="contents" value="1234567890"/>
317*876b9d75SXin Li                            <property name="permissionsFromString" value="rwxrwxrwx"/>
318*876b9d75SXin Li                            <property name="owner" value="peter"/>
319*876b9d75SXin Li                            <property name="group" value="users"/>
320*876b9d75SXin Li                        </bean>
321*876b9d75SXin Li                        <bean class="org.mockftpserver.fake.filesystem.FileEntry">
322*876b9d75SXin Li                            <property name="path" value="c:\File2.txt"/>
323*876b9d75SXin Li                            <property name="contents" value="abcdefghijklmnopqrstuvwxyz"/>
324*876b9d75SXin Li                            <property name="permissions">
325*876b9d75SXin Li                                <bean class="org.mockftpserver.fake.filesystem.Permissions">
326*876b9d75SXin Li                                    <constructor-arg value="rwx------"/>
327*876b9d75SXin Li                                </bean>
328*876b9d75SXin Li                            </property>
329*876b9d75SXin Li                            <property name="owner" value="peter"/>
330*876b9d75SXin Li                            <property name="group" value="users"/>
331*876b9d75SXin Li                        </bean>
332*876b9d75SXin Li                    </list>
333*876b9d75SXin Li                </property>
334*876b9d75SXin Li            </bean>
335*876b9d75SXin Li        </property>
336*876b9d75SXin Li
337*876b9d75SXin Li    </bean>
338*876b9d75SXin Li</beans>
339*876b9d75SXin Li+------------------------------------------------------------------------------
340*876b9d75SXin Li
341*876b9d75SXin Li
342*876b9d75SXin Li  Things to note about the above example:
343*876b9d75SXin Li
344*876b9d75SXin Li  * The <<<FakeFtpServer>>> instance is configured with a <<<WindowsFakeFileSystem>>> and a "c:\" root
345*876b9d75SXin Li    directory containing two files. Permissions and owner/group are specified for that directory, as well
346*876b9d75SXin Li    as the two predefined files contained within it.
347*876b9d75SXin Li
348*876b9d75SXin Li  * The permissions for "File1.txt" ("rwxrwxrwx") are specified using the "permissionsFromString" shortcut
349*876b9d75SXin Li    method, while the permissions for "File2.txt" ("rwx------") are specified using the "permissions" setter,
350*876b9d75SXin Li    which takes an instance of the <<<Permissions>>> class. Either method is fine.
351*876b9d75SXin Li
352*876b9d75SXin Li  []
353*876b9d75SXin Li
354*876b9d75SXin Li
355*876b9d75SXin Li* Configuring Custom CommandHandlers
356*876b9d75SXin Li~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
357*876b9d75SXin Li
358*876b9d75SXin Li  <<FakeFtpServer>> is intentionally designed to keep the lower-level details of FTP server implementation
359*876b9d75SXin Li  hidden from the user. In most cases, you can simply define the files and directories in the file
360*876b9d75SXin Li  system, configure one or more login users, and then fire up the server, expecting it to behave like
361*876b9d75SXin Li  a <real> FTP server.
362*876b9d75SXin Li
363*876b9d75SXin Li  There are some cases, however, where you might want to further customize the internal behavior of the
364*876b9d75SXin Li  server. Such cases might include:
365*876b9d75SXin Li
366*876b9d75SXin Li  * You want to have a particular FTP server command return a predetermined error reply
367*876b9d75SXin Li
368*876b9d75SXin Li  * You want to add support for a command that is not provided out of the box by <<FakeFtpServer>>
369*876b9d75SXin Li
370*876b9d75SXin Li  Note that if you need the FTP server to reply with entirely predetermined (canned) responses, then
371*876b9d75SXin Li  you may want to consider using <<StubFtpServer>> instead.
372*876b9d75SXin Li
373*876b9d75SXin Li
374*876b9d75SXin Li** Using a StaticReplyCommandHandler
375*876b9d75SXin Li~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
376*876b9d75SXin Li
377*876b9d75SXin Li  You can use one of the <CommandHandler> classes defined within the <<<org.mockftpserver.core.command>>>
378*876b9d75SXin Li  package to configure a custom <CommandHandler>. The following example uses the <<<StaticReplyCommandHandler>>>
379*876b9d75SXin Li  from that package to add support for the FEAT command. Note that in this case, we are setting the
380*876b9d75SXin Li  <CommandHandler> for a new command (i.e., one that is not supported out of the box by <<FakeFtpServer>>).
381*876b9d75SXin Li  We could just as easily set the <CommandHandler> for an existing command, overriding the default <CommandHandler>.
382*876b9d75SXin Li
383*876b9d75SXin Li+------------------------------------------------------------------------------
384*876b9d75SXin Liimport org.mockftpserver.core.command.StaticReplyCommandHandler
385*876b9d75SXin Li
386*876b9d75SXin LiFakeFtpServer ftpServer = new FakeFtpServer()
387*876b9d75SXin Li// ... set up files, directories and user accounts as usual
388*876b9d75SXin Li
389*876b9d75SXin LiStaticReplyCommandHandler featCommandHandler = new StaticReplyCommandHandler(211, "No Features");
390*876b9d75SXin LiftpServer.setCommandHandler("FEAT", featCommandHandler);
391*876b9d75SXin Li
392*876b9d75SXin Li// ...
393*876b9d75SXin LiftpServer.start()
394*876b9d75SXin Li+------------------------------------------------------------------------------
395*876b9d75SXin Li
396*876b9d75SXin Li
397*876b9d75SXin Li** Using a Stub CommandHandler
398*876b9d75SXin Li~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
399*876b9d75SXin Li
400*876b9d75SXin Li  You can also use a <<StubFtpServer>> <CommandHandler> -- i.e., one defined within the
401*876b9d75SXin Li  <<<org.mockftpserver.stub.command>>> package. The following example uses the <stub> version of the
402*876b9d75SXin Li  <<<CwdCommandHandler>>> from that package.
403*876b9d75SXin Li
404*876b9d75SXin Li+------------------------------------------------------------------------------
405*876b9d75SXin Liimport org.mockftpserver.stub.command.CwdCommandHandler
406*876b9d75SXin Li
407*876b9d75SXin LiFakeFtpServer ftpServer = new FakeFtpServer()
408*876b9d75SXin Li// ... set up files, directories and user accounts as usual
409*876b9d75SXin Li
410*876b9d75SXin Lifinal int REPLY_CODE = 502;
411*876b9d75SXin LiCwdCommandHandler cwdCommandHandler = new CwdCommandHandler();
412*876b9d75SXin LicwdCommandHandler.setReplyCode(REPLY_CODE);
413*876b9d75SXin LiftpServer.setCommandHandler(CommandNames.CWD, cwdCommandHandler);
414*876b9d75SXin Li
415*876b9d75SXin Li// ...
416*876b9d75SXin LiftpServer.start()
417*876b9d75SXin Li+------------------------------------------------------------------------------
418*876b9d75SXin Li
419*876b9d75SXin Li
420*876b9d75SXin Li** Creating Your Own Custom CommandHandler Class
421*876b9d75SXin Li~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
422*876b9d75SXin Li
423*876b9d75SXin Li  If one of the existing <CommandHandler> classes does not fulfill your needs, you can alternately create
424*876b9d75SXin Li  your own custom <CommandHandler> class. The only requirement is that it implement the
425*876b9d75SXin Li  <<<org.mockftpserver.core.command.CommandHandler>>> interface. You would, however, likely benefit from
426*876b9d75SXin Li  inheriting from one of the existing abstract <CommandHandler> superclasses, such as
427*876b9d75SXin Li  <<<org.mockftpserver.core.command.AbstractStaticReplyCommandHandler>>> or
428*876b9d75SXin Li  <<<org.mockftpserver.core.command.AbstractCommandHandler>>>. See the javadoc of these classes for
429*876b9d75SXin Li  more information.
430*876b9d75SXin Li
431*876b9d75SXin Li
432*876b9d75SXin Li* FTP Command Reply Text ResourceBundle
433*876b9d75SXin Li~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
434*876b9d75SXin Li
435*876b9d75SXin Li  The default text asociated with each FTP command reply code is contained within the
436*876b9d75SXin Li  "ReplyText.properties" ResourceBundle file. You can customize these messages by providing a
437*876b9d75SXin Li  locale-specific ResourceBundle file on the CLASSPATH, according to the normal lookup rules of
438*876b9d75SXin Li  the ResourceBundle class (e.g., "ReplyText_de.properties"). Alternatively, you can
439*876b9d75SXin Li  completely replace the ResourceBundle file by calling the calling the
440*876b9d75SXin Li  <<<FakeFtpServer.setReplyTextBaseName(String)>>> method.
441*876b9d75SXin Li
442*876b9d75SXin Li* SLF4J Configuration Required to See Log Output
443*876b9d75SXin Li~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
444*876b9d75SXin Li
445*876b9d75SXin Li  Note that <<FakeFtpServer>> uses {{{http://www.slf4j.org/}SLF4J}} for logging. If you want to
446*876b9d75SXin Li  see the logging output, then you must configure <<SLF4J>>. (If no binding is found on the class
447*876b9d75SXin Li  path, then <<SLF4J>> will default to a no-operation implementation.)
448*876b9d75SXin Li
449*876b9d75SXin Li  See the {{{http://www.slf4j.org/manual.html}SLF4J User Manual}} for more information.
450