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