summaryrefslogtreecommitdiff
path: root/tags
diff options
context:
space:
mode:
authorchrismair <chrismair@531de8e6-9941-0410-b38b-9a92acbe0330>2008-08-14 01:46:09 +0000
committerchrismair <chrismair@531de8e6-9941-0410-b38b-9a92acbe0330>2008-08-14 01:46:09 +0000
commit4531116f8e675a208710e987bfe3b58faeb12db2 (patch)
tree4cf910533ce03bdda49deee07462dcebda8c9d0c /tags
parentef816841d62a64bd05044fbfa0d032d62304f3ec (diff)
downloadmockftpserver-4531116f8e675a208710e987bfe3b58faeb12db2.tar.gz
Version 1.2.3
git-svn-id: svn://svn.code.sf.net/p/mockftpserver/code@95 531de8e6-9941-0410-b38b-9a92acbe0330
Diffstat (limited to 'tags')
-rw-r--r--tags/1.2.3/.classpath21
-rw-r--r--tags/1.2.3/.project17
-rw-r--r--tags/1.2.3/.settings/org.eclipse.jdt.core.prefs268
-rw-r--r--tags/1.2.3/.settings/org.eclipse.jdt.ui.prefs5
-rw-r--r--tags/1.2.3/CHANGELOG.txt45
-rw-r--r--tags/1.2.3/LICENSE.txt201
-rw-r--r--tags/1.2.3/README.txt27
-rw-r--r--tags/1.2.3/pom.xml192
-rw-r--r--tags/1.2.3/src/assembly/assembly.xml50
-rw-r--r--tags/1.2.3/src/main/java/org/mockftpserver/core/MockFtpServerException.java52
-rw-r--r--tags/1.2.3/src/main/java/org/mockftpserver/core/command/AbstractCommandHandler.java251
-rw-r--r--tags/1.2.3/src/main/java/org/mockftpserver/core/command/Command.java138
-rw-r--r--tags/1.2.3/src/main/java/org/mockftpserver/core/command/CommandHandler.java42
-rw-r--r--tags/1.2.3/src/main/java/org/mockftpserver/core/command/CommandNames.java70
-rw-r--r--tags/1.2.3/src/main/java/org/mockftpserver/core/command/InvocationHistory.java50
-rw-r--r--tags/1.2.3/src/main/java/org/mockftpserver/core/command/InvocationRecord.java183
-rw-r--r--tags/1.2.3/src/main/java/org/mockftpserver/core/command/ReplyCodes.java68
-rw-r--r--tags/1.2.3/src/main/java/org/mockftpserver/core/command/ReplyTextBundleAware.java43
-rw-r--r--tags/1.2.3/src/main/java/org/mockftpserver/core/command/ReplyTextBundleUtil.java53
-rw-r--r--tags/1.2.3/src/main/java/org/mockftpserver/core/command/SimpleCompositeCommandHandler.java129
-rw-r--r--tags/1.2.3/src/main/java/org/mockftpserver/core/command/StaticReplyCommandHandler.java70
-rw-r--r--tags/1.2.3/src/main/java/org/mockftpserver/core/session/DefaultSession.java476
-rw-r--r--tags/1.2.3/src/main/java/org/mockftpserver/core/session/Session.java128
-rw-r--r--tags/1.2.3/src/main/java/org/mockftpserver/core/socket/DefaultServerSocketFactory.java41
-rw-r--r--tags/1.2.3/src/main/java/org/mockftpserver/core/socket/DefaultSocketFactory.java43
-rw-r--r--tags/1.2.3/src/main/java/org/mockftpserver/core/socket/ServerSocketFactory.java38
-rw-r--r--tags/1.2.3/src/main/java/org/mockftpserver/core/socket/SocketFactory.java40
-rw-r--r--tags/1.2.3/src/main/java/org/mockftpserver/core/util/Assert.java138
-rw-r--r--tags/1.2.3/src/main/java/org/mockftpserver/core/util/AssertFailedException.java36
-rw-r--r--tags/1.2.3/src/main/java/org/mockftpserver/stub/StubFtpServer.java403
-rw-r--r--tags/1.2.3/src/main/java/org/mockftpserver/stub/command/AborCommandHandler.java49
-rw-r--r--tags/1.2.3/src/main/java/org/mockftpserver/stub/command/AbstractStubCommandHandler.java102
-rw-r--r--tags/1.2.3/src/main/java/org/mockftpserver/stub/command/AbstractStubDataCommandHandler.java229
-rw-r--r--tags/1.2.3/src/main/java/org/mockftpserver/stub/command/AcctCommandHandler.java55
-rw-r--r--tags/1.2.3/src/main/java/org/mockftpserver/stub/command/AlloCommandHandler.java75
-rw-r--r--tags/1.2.3/src/main/java/org/mockftpserver/stub/command/AppeCommandHandler.java63
-rw-r--r--tags/1.2.3/src/main/java/org/mockftpserver/stub/command/CdupCommandHandler.java49
-rw-r--r--tags/1.2.3/src/main/java/org/mockftpserver/stub/command/ConnectCommandHandler.java54
-rw-r--r--tags/1.2.3/src/main/java/org/mockftpserver/stub/command/CwdCommandHandler.java55
-rw-r--r--tags/1.2.3/src/main/java/org/mockftpserver/stub/command/DeleCommandHandler.java55
-rw-r--r--tags/1.2.3/src/main/java/org/mockftpserver/stub/command/FileRetrCommandHandler.java113
-rw-r--r--tags/1.2.3/src/main/java/org/mockftpserver/stub/command/HelpCommandHandler.java68
-rw-r--r--tags/1.2.3/src/main/java/org/mockftpserver/stub/command/ListCommandHandler.java75
-rw-r--r--tags/1.2.3/src/main/java/org/mockftpserver/stub/command/MkdCommandHandler.java56
-rw-r--r--tags/1.2.3/src/main/java/org/mockftpserver/stub/command/ModeCommandHandler.java55
-rw-r--r--tags/1.2.3/src/main/java/org/mockftpserver/stub/command/NlstCommandHandler.java68
-rw-r--r--tags/1.2.3/src/main/java/org/mockftpserver/stub/command/NoopCommandHandler.java49
-rw-r--r--tags/1.2.3/src/main/java/org/mockftpserver/stub/command/PassCommandHandler.java55
-rw-r--r--tags/1.2.3/src/main/java/org/mockftpserver/stub/command/PasvCommandHandler.java95
-rw-r--r--tags/1.2.3/src/main/java/org/mockftpserver/stub/command/PortCommandHandler.java153
-rw-r--r--tags/1.2.3/src/main/java/org/mockftpserver/stub/command/PwdCommandHandler.java61
-rw-r--r--tags/1.2.3/src/main/java/org/mockftpserver/stub/command/QuitCommandHandler.java50
-rw-r--r--tags/1.2.3/src/main/java/org/mockftpserver/stub/command/ReinCommandHandler.java50
-rw-r--r--tags/1.2.3/src/main/java/org/mockftpserver/stub/command/RestCommandHandler.java56
-rw-r--r--tags/1.2.3/src/main/java/org/mockftpserver/stub/command/RetrCommandHandler.java108
-rw-r--r--tags/1.2.3/src/main/java/org/mockftpserver/stub/command/RmdCommandHandler.java55
-rw-r--r--tags/1.2.3/src/main/java/org/mockftpserver/stub/command/RnfrCommandHandler.java55
-rw-r--r--tags/1.2.3/src/main/java/org/mockftpserver/stub/command/RntoCommandHandler.java55
-rw-r--r--tags/1.2.3/src/main/java/org/mockftpserver/stub/command/SiteCommandHandler.java55
-rw-r--r--tags/1.2.3/src/main/java/org/mockftpserver/stub/command/SmntCommandHandler.java55
-rw-r--r--tags/1.2.3/src/main/java/org/mockftpserver/stub/command/StatCommandHandler.java80
-rw-r--r--tags/1.2.3/src/main/java/org/mockftpserver/stub/command/StorCommandHandler.java63
-rw-r--r--tags/1.2.3/src/main/java/org/mockftpserver/stub/command/StouCommandHandler.java76
-rw-r--r--tags/1.2.3/src/main/java/org/mockftpserver/stub/command/StruCommandHandler.java55
-rw-r--r--tags/1.2.3/src/main/java/org/mockftpserver/stub/command/SystCommandHandler.java67
-rw-r--r--tags/1.2.3/src/main/java/org/mockftpserver/stub/command/TypeCommandHandler.java62
-rw-r--r--tags/1.2.3/src/main/java/org/mockftpserver/stub/command/UserCommandHandler.java89
-rw-r--r--tags/1.2.3/src/main/resources/ReplyText.properties62
-rw-r--r--tags/1.2.3/src/site/apt/index.apt30
-rw-r--r--tags/1.2.3/src/site/apt/stubftpserver-commandhandlers.apt89
-rw-r--r--tags/1.2.3/src/site/apt/stubftpserver-features.apt32
-rw-r--r--tags/1.2.3/src/site/apt/stubftpserver-getting-started.apt312
-rw-r--r--tags/1.2.3/src/site/fml/faq.fml51
-rw-r--r--tags/1.2.3/src/site/resources/css/site.css4
-rw-r--r--tags/1.2.3/src/site/site.xml43
-rw-r--r--tags/1.2.3/src/test/java/org/mockftpserver/core/command/InvocationRecordTest.java228
-rw-r--r--tags/1.2.3/src/test/java/org/mockftpserver/core/command/ReplyTextBundleUtilTest.java103
-rw-r--r--tags/1.2.3/src/test/java/org/mockftpserver/core/command/SimpleCompositeCommandHandlerTest.java272
-rw-r--r--tags/1.2.3/src/test/java/org/mockftpserver/core/command/StaticReplyCommandHandlerTest.java142
-rw-r--r--tags/1.2.3/src/test/java/org/mockftpserver/core/command/_AbstractCommandHandlerTest.java269
-rw-r--r--tags/1.2.3/src/test/java/org/mockftpserver/core/session/DefaultSessionTest.java454
-rw-r--r--tags/1.2.3/src/test/java/org/mockftpserver/core/session/DefaultSession_RunTest.java246
-rw-r--r--tags/1.2.3/src/test/java/org/mockftpserver/core/socket/StubServerSocket.java74
-rw-r--r--tags/1.2.3/src/test/java/org/mockftpserver/core/socket/StubServerSocketFactory.java50
-rw-r--r--tags/1.2.3/src/test/java/org/mockftpserver/core/socket/StubSocket.java101
-rw-r--r--tags/1.2.3/src/test/java/org/mockftpserver/core/socket/StubSocketFactory.java55
-rw-r--r--tags/1.2.3/src/test/java/org/mockftpserver/core/util/AssertTest.java230
-rw-r--r--tags/1.2.3/src/test/java/org/mockftpserver/stub/StubFtpServerIntegrationTest.java639
-rw-r--r--tags/1.2.3/src/test/java/org/mockftpserver/stub/StubFtpServerTest.java184
-rw-r--r--tags/1.2.3/src/test/java/org/mockftpserver/stub/StubFtpServer_MultipleClientsIntegrationTest.java137
-rw-r--r--tags/1.2.3/src/test/java/org/mockftpserver/stub/StubFtpServer_StartTest.java82
-rw-r--r--tags/1.2.3/src/test/java/org/mockftpserver/stub/command/AborCommandHandlerTest.java60
-rw-r--r--tags/1.2.3/src/test/java/org/mockftpserver/stub/command/AbstractCommandHandlerTest.java231
-rw-r--r--tags/1.2.3/src/test/java/org/mockftpserver/stub/command/AcctCommandHandlerTest.java76
-rw-r--r--tags/1.2.3/src/test/java/org/mockftpserver/stub/command/AlloCommandHandlerTest.java123
-rw-r--r--tags/1.2.3/src/test/java/org/mockftpserver/stub/command/AppeCommandHandlerTest.java75
-rw-r--r--tags/1.2.3/src/test/java/org/mockftpserver/stub/command/CdupCommandHandlerTest.java65
-rw-r--r--tags/1.2.3/src/test/java/org/mockftpserver/stub/command/CommandTest.java161
-rw-r--r--tags/1.2.3/src/test/java/org/mockftpserver/stub/command/ConnectCommandHandlerTest.java62
-rw-r--r--tags/1.2.3/src/test/java/org/mockftpserver/stub/command/CwdCommandHandlerTest.java72
-rw-r--r--tags/1.2.3/src/test/java/org/mockftpserver/stub/command/DeleCommandHandlerTest.java72
-rw-r--r--tags/1.2.3/src/test/java/org/mockftpserver/stub/command/FileRetrCommandHandlerTest.java179
-rw-r--r--tags/1.2.3/src/test/java/org/mockftpserver/stub/command/HelpCommandHandlerTest.java69
-rw-r--r--tags/1.2.3/src/test/java/org/mockftpserver/stub/command/ListCommandHandlerTest.java76
-rw-r--r--tags/1.2.3/src/test/java/org/mockftpserver/stub/command/MkdCommandHandlerTest.java75
-rw-r--r--tags/1.2.3/src/test/java/org/mockftpserver/stub/command/ModeCommandHandlerTest.java77
-rw-r--r--tags/1.2.3/src/test/java/org/mockftpserver/stub/command/NlstCommandHandlerTest.java74
-rw-r--r--tags/1.2.3/src/test/java/org/mockftpserver/stub/command/NoopCommandHandlerTest.java61
-rw-r--r--tags/1.2.3/src/test/java/org/mockftpserver/stub/command/PassCommandHandlerTest.java76
-rw-r--r--tags/1.2.3/src/test/java/org/mockftpserver/stub/command/PasvCommandHandlerTest.java84
-rw-r--r--tags/1.2.3/src/test/java/org/mockftpserver/stub/command/PortCommandHandlerTest.java156
-rw-r--r--tags/1.2.3/src/test/java/org/mockftpserver/stub/command/PwdCommandHandlerTest.java65
-rw-r--r--tags/1.2.3/src/test/java/org/mockftpserver/stub/command/QuitCommandHandlerTest.java62
-rw-r--r--tags/1.2.3/src/test/java/org/mockftpserver/stub/command/ReinCommandHandlerTest.java64
-rw-r--r--tags/1.2.3/src/test/java/org/mockftpserver/stub/command/RestCommandHandlerTest.java78
-rw-r--r--tags/1.2.3/src/test/java/org/mockftpserver/stub/command/RetrCommandHandlerTest.java132
-rw-r--r--tags/1.2.3/src/test/java/org/mockftpserver/stub/command/RmdCommandHandlerTest.java72
-rw-r--r--tags/1.2.3/src/test/java/org/mockftpserver/stub/command/RnfrCommandHandlerTest.java74
-rw-r--r--tags/1.2.3/src/test/java/org/mockftpserver/stub/command/RntoCommandHandlerTest.java74
-rw-r--r--tags/1.2.3/src/test/java/org/mockftpserver/stub/command/SiteCommandHandlerTest.java74
-rw-r--r--tags/1.2.3/src/test/java/org/mockftpserver/stub/command/SmntCommandHandlerTest.java71
-rw-r--r--tags/1.2.3/src/test/java/org/mockftpserver/stub/command/StatCommandHandlerTest.java103
-rw-r--r--tags/1.2.3/src/test/java/org/mockftpserver/stub/command/StorCommandHandlerTest.java75
-rw-r--r--tags/1.2.3/src/test/java/org/mockftpserver/stub/command/StouCommandHandlerTest.java69
-rw-r--r--tags/1.2.3/src/test/java/org/mockftpserver/stub/command/StruCommandHandlerTest.java77
-rw-r--r--tags/1.2.3/src/test/java/org/mockftpserver/stub/command/SystCommandHandlerTest.java81
-rw-r--r--tags/1.2.3/src/test/java/org/mockftpserver/stub/command/TypeCommandHandlerTest.java75
-rw-r--r--tags/1.2.3/src/test/java/org/mockftpserver/stub/command/UserCommandHandlerTest.java87
-rw-r--r--tags/1.2.3/src/test/java/org/mockftpserver/stub/command/_AbstractStubCommandHandlerTest.java160
-rw-r--r--tags/1.2.3/src/test/java/org/mockftpserver/stub/command/_AbstractStubDataCommandHandlerTest.java177
-rw-r--r--tags/1.2.3/src/test/java/org/mockftpserver/stub/example/FtpWorkingDirectory.java63
-rw-r--r--tags/1.2.3/src/test/java/org/mockftpserver/stub/example/FtpWorkingDirectoryTest.java70
-rw-r--r--tags/1.2.3/src/test/java/org/mockftpserver/stub/example/RemoteFile.java68
-rw-r--r--tags/1.2.3/src/test/java/org/mockftpserver/stub/example/RemoteFileTest.java104
-rw-r--r--tags/1.2.3/src/test/java/org/mockftpserver/stub/example/SpringConfigurationTest.java88
-rw-r--r--tags/1.2.3/src/test/java/org/mockftpserver/test/AbstractDirectoryTestSuite.java133
-rw-r--r--tags/1.2.3/src/test/java/org/mockftpserver/test/AbstractTest.java355
-rw-r--r--tags/1.2.3/src/test/java/org/mockftpserver/test/AbstractTestSuite.java57
-rw-r--r--tags/1.2.3/src/test/java/org/mockftpserver/test/IntegrationTest.java27
-rw-r--r--tags/1.2.3/src/test/java/org/mockftpserver/test/LoggingUtil.java147
-rw-r--r--tags/1.2.3/src/test/java/org/mockftpserver/test/MockFtpServerTestSuite.java54
-rw-r--r--tags/1.2.3/src/test/java/org/mockftpserver/test/PortTestUtil.java43
-rw-r--r--tags/1.2.3/src/test/resources/Sample.data1
-rw-r--r--tags/1.2.3/src/test/resources/SampleReplyText.properties17
-rw-r--r--tags/1.2.3/src/test/resources/log4j.properties6
-rw-r--r--tags/1.2.3/src/test/resources/stubftpserver-beans.xml66
146 files changed, 14765 insertions, 0 deletions
diff --git a/tags/1.2.3/.classpath b/tags/1.2.3/.classpath
new file mode 100644
index 0000000..c0a3d65
--- /dev/null
+++ b/tags/1.2.3/.classpath
@@ -0,0 +1,21 @@
+<classpath>
+ <classpathentry kind="src" path="src/main/java"/>
+ <classpathentry kind="src" path="src/main/resources" excluding="**/*.java"/>
+ <classpathentry kind="src" path="src/test/java" output="target/test-classes"/>
+ <classpathentry kind="src" path="src/test/resources" output="target/test-classes" excluding="**/*.java"/>
+ <classpathentry kind="output" path="target/classes"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+ <classpathentry kind="var" path="M2_REPO/junit-addons/junit-addons/1.4/junit-addons-1.4.jar"/>
+ <classpathentry kind="var" path="M2_REPO/log4j/log4j/1.2.13/log4j-1.2.13.jar"/>
+ <classpathentry kind="var" path="M2_REPO/commons-net/commons-net/1.4.1/commons-net-1.4.1.jar"/>
+ <classpathentry kind="var" path="M2_REPO/avalon-framework/avalon-framework/4.1.3/avalon-framework-4.1.3.jar"/>
+ <classpathentry kind="var" path="M2_REPO/junit/junit/3.8.1/junit-3.8.1.jar"/>
+ <classpathentry kind="var" path="M2_REPO/xerces/xercesImpl/2.6.2/xercesImpl-2.6.2.jar"/>
+ <classpathentry kind="var" path="M2_REPO/commons-logging/commons-logging/1.1/commons-logging-1.1.jar"/>
+ <classpathentry kind="var" path="M2_REPO/oro/oro/2.0.8/oro-2.0.8.jar"/>
+ <classpathentry kind="var" path="M2_REPO/javax/servlet/servlet-api/2.3/servlet-api-2.3.jar"/>
+ <classpathentry kind="var" path="M2_REPO/logkit/logkit/1.0.1/logkit-1.0.1.jar"/>
+ <classpathentry kind="var" path="M2_REPO/org/springframework/spring/2.0.7/spring-2.0.7.jar"/>
+ <classpathentry kind="var" path="M2_REPO/easymock/easymock/1.2_Java1.3/easymock-1.2_Java1.3.jar"/>
+ <classpathentry kind="var" path="M2_REPO/xerces/xmlParserAPIs/2.6.2/xmlParserAPIs-2.6.2.jar"/>
+</classpath> \ No newline at end of file
diff --git a/tags/1.2.3/.project b/tags/1.2.3/.project
new file mode 100644
index 0000000..f71e9bd
--- /dev/null
+++ b/tags/1.2.3/.project
@@ -0,0 +1,17 @@
+<projectDescription>
+ <name>MockFtpServer</name>
+ <comment/>
+ <projects/>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ </buildCommand>
+ <buildCommand>
+ <name>org.maven.ide.eclipse.maven2Builder</name>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ <nature>org.maven.ide.eclipse.maven2Nature</nature>
+ </natures>
+</projectDescription> \ No newline at end of file
diff --git a/tags/1.2.3/.settings/org.eclipse.jdt.core.prefs b/tags/1.2.3/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..900b691
--- /dev/null
+++ b/tags/1.2.3/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,268 @@
+#Thu Jan 31 20:14:07 EST 2008
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.2
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=1.4
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=warning
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=warning
+org.eclipse.jdt.core.compiler.source=1.3
+org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_assignment=0
+org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_compact_if=16
+org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80
+org.eclipse.jdt.core.formatter.alignment_for_enum_constants=0
+org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16
+org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16
+org.eclipse.jdt.core.formatter.blank_lines_after_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_after_package=1
+org.eclipse.jdt.core.formatter.blank_lines_before_field=0
+org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0
+org.eclipse.jdt.core.formatter.blank_lines_before_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_before_member_type=1
+org.eclipse.jdt.core.formatter.blank_lines_before_method=1
+org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1
+org.eclipse.jdt.core.formatter.blank_lines_before_package=0
+org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1
+org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1
+org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines=false
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false
+org.eclipse.jdt.core.formatter.comment.format_block_comments=true
+org.eclipse.jdt.core.formatter.comment.format_comments=true
+org.eclipse.jdt.core.formatter.comment.format_header=false
+org.eclipse.jdt.core.formatter.comment.format_html=true
+org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true
+org.eclipse.jdt.core.formatter.comment.format_line_comments=true
+org.eclipse.jdt.core.formatter.comment.format_source_code=true
+org.eclipse.jdt.core.formatter.comment.indent_parameter_description=false
+org.eclipse.jdt.core.formatter.comment.indent_root_tags=true
+org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert
+org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=do not insert
+org.eclipse.jdt.core.formatter.comment.line_length=100
+org.eclipse.jdt.core.formatter.compact_else_if=true
+org.eclipse.jdt.core.formatter.continuation_indentation=2
+org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2
+org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true
+org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_empty_lines=false
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=false
+org.eclipse.jdt.core.formatter.indentation.size=4
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert
+org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.lineSplit=120
+org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0
+org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1
+org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true
+org.eclipse.jdt.core.formatter.tabulation.char=space
+org.eclipse.jdt.core.formatter.tabulation.size=4
+org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false
+org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true
diff --git a/tags/1.2.3/.settings/org.eclipse.jdt.ui.prefs b/tags/1.2.3/.settings/org.eclipse.jdt.ui.prefs
new file mode 100644
index 0000000..538a82b
--- /dev/null
+++ b/tags/1.2.3/.settings/org.eclipse.jdt.ui.prefs
@@ -0,0 +1,5 @@
+#Thu Jan 31 20:14:06 EST 2008
+eclipse.preferences.version=1
+formatter_profile=_Chris Profile
+formatter_settings_version=11
+internal.default.compliance=default
diff --git a/tags/1.2.3/CHANGELOG.txt b/tags/1.2.3/CHANGELOG.txt
new file mode 100644
index 0000000..f622289
--- /dev/null
+++ b/tags/1.2.3/CHANGELOG.txt
@@ -0,0 +1,45 @@
+MockFtpServer Change Log
+-------------------------------------------------------------------------------
+
+Changes in version 1.2.3 (13 Aug 2008)
+------------------------------------------
+- BUG FIX: Tracker item #2047355. Parse host IP numbers as unsigned bytes.
+ See https://sourceforge.net/tracker/index.php?func=detail&aid=2047355&group_id=208647&atid=1006533
+
+
+Changes in version 1.2.2 (27 May 2008)
+------------------------------------------
+- BUG FIX: Move serverThread.start() into synchronized block to avoid server hang if
+ server thread runs faster than main thread.
+ See https://sourceforge.net/tracker/?func=detail&atid=1006533&aid=1925590&group_id=208647
+
+
+Changes in version 1.2.1 (10 Mar 2008)
+------------------------------------------
+- Change Maven POM (pom.xml) to enable sync-ing with central Maven repository (ibiblio).
+
+Changes in version 1.2 (29 Feb 2008)
+------------------------------------------
+- BUG FIX: StubFtpServer: Add wait/notify to ensure that the server starts up and opens the server
+ control port before the start() method returns. This fixes a potential race condition, which
+ shows up on some Linux systems. (Thanks to Aasman Bajaj for identifying the problem and providing the fix)
+- Modify tests to make server port configurable (through "ftp.server.port" system property), allowing
+ tests to run on non-Windows systems.
+
+
+Changes in version 1.1 (20 Feb 2008)
+------------------------------------------
+- StubFtpServer: Allow configuring server control connection port other than the default (21).
+- AbstractTest: Add some test convenience methods.
+
+
+Changes in version 1.0 final (11 Dec 2007)
+------------------------------------------
+- Implement default CommandHandlers for NLST, REIN, SMNT, SITE, ABOR and ALLO commands.
+- Handle command names in any case.
+- CwdCommandHandler: Fix PATHNAME_KEY constant value; change to "pathname".
+
+
+Changes in version 1.0-RC1 (1 Nov 2007)
+---------------------------------------
+Initial release. \ No newline at end of file
diff --git a/tags/1.2.3/LICENSE.txt b/tags/1.2.3/LICENSE.txt
new file mode 100644
index 0000000..29f81d8
--- /dev/null
+++ b/tags/1.2.3/LICENSE.txt
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/tags/1.2.3/README.txt b/tags/1.2.3/README.txt
new file mode 100644
index 0000000..45e7e0a
--- /dev/null
+++ b/tags/1.2.3/README.txt
@@ -0,0 +1,27 @@
+MockFtpServer version ${project.version}
+-------------------------------------------------------------------------------
+${project.url}
+
+The MockFtpServer project provides a mock/dummy FTP server implementation that can be very
+useful for testing of FTP client code. It can be configured to return custom data or reply
+codes, to simulate either success or failure scenarios. Expected command invocations can
+also be verified.
+
+MockFtpServer is written in Java, and is ideally suited to testing Java code. But because
+communication with the FTP server is across sockets and TCP/IP, it can be used to test FTP client
+code written in any language.
+
+The MockFtpServer project may one day provide multiple mock/dummy FTP server implementations,
+at different levels of abstraction. Currently, however, StubFtpServer is the only one provided,
+though others are being considered. StubFtpServer is a "stub" implementation of an FTP server.
+See the "StubFtpServer Getting Started Guide" for more information.
+
+See the FTP Protocol Spec (http://www.ietf.org/rfc/rfc0959.txt) for information about
+FTP, commands, reply codes, etc..
+
+DEPENDENCIES
+
+MockFtpServer requires
+ - Java (JDK) version 1.3 or later
+ - The Log4J jar, version 1.2.13 or later, accessible on the CLASSPATH
+ (http://logging.apache.org/log4j/index.html).
diff --git a/tags/1.2.3/pom.xml b/tags/1.2.3/pom.xml
new file mode 100644
index 0000000..86c0ace
--- /dev/null
+++ b/tags/1.2.3/pom.xml
@@ -0,0 +1,192 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>org.mockftpserver</groupId>
+ <artifactId>MockFtpServer</artifactId>
+ <name>MockFtpServer</name>
+ <description>
+ The MockFtpServer project provides a mock/dummy FTP server for testing FTP client code.
+ It can be configured to return custom data or reply codes, to simulate either success
+ or failure scenarios. You can also verify expected command invocations.
+ </description>
+ <packaging>jar</packaging>
+ <version>1.2.3</version>
+ <url>http://mockftpserver.sourceforge.net/</url>
+
+ <scm>
+ <connection>scm:svn:https://mockftpserver.svn.sourceforge.net/svnroot/mockftpserver/MockFtpServer</connection>
+ <developerConnection>scm:svn:https://mockftpserver.svn.sourceforge.net/svnroot/mockftpserver/MockFtpServer</developerConnection>
+ <url>https://mockftpserver.svn.sourceforge.net/svnroot/mockftpserver</url>
+ </scm>
+
+ <dependencies>
+
+ <dependency>
+ <groupId>log4j</groupId>
+ <artifactId>log4j</artifactId>
+ <version>1.2.13</version>
+ </dependency>
+
+ <!-- TESTING ONLY -->
+
+ <dependency>
+ <groupId>commons-net</groupId>
+ <artifactId>commons-net</artifactId>
+ <version>1.4.1</version>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>easymock</groupId>
+ <artifactId>easymock</artifactId>
+ <version>1.2_Java1.3</version>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>junit-addons</groupId>
+ <artifactId>junit-addons</artifactId>
+ <version>1.4</version>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring</artifactId>
+ <version>2.0.7</version>
+ <scope>test</scope>
+ </dependency>
+
+ <!-- Transitive dependency.
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <version>3.8.1</version>
+ <scope>test</scope>
+ </dependency>
+ -->
+
+ </dependencies>
+
+ <distributionManagement>
+ <repository>
+ <id>sourceforge</id>
+ <url>scp://shell.sourceforge.net/home/groups/m/mo/mockftpserver/htdocs/m2repo</url>
+ </repository>
+ </distributionManagement>
+
+ <build>
+ <extensions>
+ <extension>
+ <groupId>org.apache.maven.wagon</groupId>
+ <artifactId>wagon-ssh-external</artifactId>
+ <version>1.0-beta-2</version>
+ </extension>
+ </extensions>
+
+ <plugins>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <configuration>
+ <verbose>true</verbose>
+ <fork>true</fork>
+ <executable>${JAVA_1_4_HOME}/bin/javac</executable>
+ <compilerVersion>1.3</compilerVersion>
+ </configuration>
+ </plugin>
+
+ <!-- clean coverage data before collecting -->
+ <plugin>
+ <artifactId>cobertura-maven-plugin</artifactId>
+ <groupId>org.codehaus.mojo</groupId>
+ <version>2.0</version>
+ <executions>
+ <execution>
+ <goals>
+ <goal>clean</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-jar-plugin</artifactId>
+ <configuration>
+ <archive>
+ <manifestEntries>
+ <MockFtpServer-Version>${pom.version}</MockFtpServer-Version>
+ </manifestEntries>
+ </archive>
+ </configuration>
+ </plugin>
+
+ <plugin>
+ <artifactId>maven-assembly-plugin</artifactId>
+ <configuration>
+ <descriptors>
+ <descriptor>src/assembly/assembly.xml</descriptor>
+ </descriptors>
+ </configuration>
+ </plugin>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-release-plugin</artifactId>
+ <configuration>
+ <preparationGoals>clean site assembly:assembly</preparationGoals>
+ </configuration>
+ </plugin>
+
+ </plugins>
+ </build>
+
+ <reporting>
+ <plugins>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-project-info-reports-plugin</artifactId>
+ <reportSets>
+ <reportSet>
+ <reports>
+ <report>dependencies</report>
+ <!-- <report>project-team</report> -->
+ <!-- <report>mailing-list</report> -->
+ <!-- <report>cim</report> -->
+ <!-- <report>issue-tracking</report> -->
+ <report>license</report>
+ <!-- <report>scm</report> -->
+ </reports>
+ </reportSet>
+ </reportSets>
+ </plugin>
+
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>cobertura-maven-plugin</artifactId>
+ </plugin>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-javadoc-plugin</artifactId>
+ </plugin>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-pmd-plugin</artifactId>
+ </plugin>
+
+ </plugins>
+ </reporting>
+
+ <licenses>
+ <license>
+ <name>Apache 2</name>
+ <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
+ <distribution>repo</distribution>
+ </license>
+ </licenses>
+
+</project> \ No newline at end of file
diff --git a/tags/1.2.3/src/assembly/assembly.xml b/tags/1.2.3/src/assembly/assembly.xml
new file mode 100644
index 0000000..870cc69
--- /dev/null
+++ b/tags/1.2.3/src/assembly/assembly.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0"?>
+<assembly>
+ <id>bin</id>
+ <formats>
+ <format>tar.gz</format>
+ <format>zip</format>
+ </formats>
+ <includeSiteDirectory>false</includeSiteDirectory>
+
+ <files>
+ <file>
+ <source>README.txt</source>
+ <outputDirectory>/</outputDirectory>
+ <filtered>true</filtered>
+ </file>
+ </files>
+
+ <fileSets>
+ <fileSet>
+ <includes>
+ <include>CHANGELOG*</include>
+ <include>LICENSE*</include>
+ <include>NOTICE*</include>
+ <include>pom.xml</include>
+ </includes>
+ </fileSet>
+ <fileSet>
+ <directory>target</directory>
+ <outputDirectory></outputDirectory>
+ <includes>
+ <include>*.jar</include>
+ </includes>
+ </fileSet>
+ <fileSet>
+ <directory>src</directory>
+ <useDefaultExcludes>true</useDefaultExcludes>
+ </fileSet>
+ <fileSet>
+ <directory>target/site</directory>
+ <outputDirectory>docs</outputDirectory>
+ <includes>
+ <include>apidocs/**</include>
+ <include>css/**</include>
+ <include>images/**</include>
+ <include>stubftpserver-*.html</include>
+ <include>index.html</include>
+ </includes>
+ </fileSet>
+ </fileSets>
+</assembly>
diff --git a/tags/1.2.3/src/main/java/org/mockftpserver/core/MockFtpServerException.java b/tags/1.2.3/src/main/java/org/mockftpserver/core/MockFtpServerException.java
new file mode 100644
index 0000000..d412ecd
--- /dev/null
+++ b/tags/1.2.3/src/main/java/org/mockftpserver/core/MockFtpServerException.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.core;
+
+/**
+ * Represents an error specific to the MockFtpServer project.
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public class MockFtpServerException extends RuntimeException {
+
+ /**
+ * Create a new instance with the specified detail message and no cause
+ * @param message - the exception detail message
+ */
+ public MockFtpServerException(String message) {
+ super(message);
+ }
+
+ /**
+ * Create a new instance with the specified detail message and no cause
+ * @param cause - the Throwable cause
+ */
+ public MockFtpServerException(Throwable cause) {
+ super(cause);
+ }
+
+ /**
+ * Create a new instance with the specified detail message and cause
+ * @param message - the exception detail message
+ * @param cause - the Throwable cause
+ */
+ public MockFtpServerException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+}
diff --git a/tags/1.2.3/src/main/java/org/mockftpserver/core/command/AbstractCommandHandler.java b/tags/1.2.3/src/main/java/org/mockftpserver/core/command/AbstractCommandHandler.java
new file mode 100644
index 0000000..df96df7
--- /dev/null
+++ b/tags/1.2.3/src/main/java/org/mockftpserver/core/command/AbstractCommandHandler.java
@@ -0,0 +1,251 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.core.command;
+
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+import org.apache.log4j.Logger;
+import org.mockftpserver.core.session.Session;
+import org.mockftpserver.core.util.Assert;
+import org.mockftpserver.core.util.AssertFailedException;
+
+/**
+ * The abstract superclass for CommandHandler classes. This class manages the List of
+ * InvocationRecord objects corresponding to each invocation of the command handler,
+ * and provides helper methods for subclasses.
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public abstract class AbstractCommandHandler implements CommandHandler, ReplyTextBundleAware, InvocationHistory {
+
+ private static final Logger LOG = Logger.getLogger(AbstractCommandHandler.class);
+
+ private ResourceBundle replyTextBundle;
+ private List invocations = new ArrayList();
+
+ // -------------------------------------------------------------------------
+ // Template Method
+ // -------------------------------------------------------------------------
+
+ /**
+ * Handle the specified command for the session. This method is declared to throw Exception,
+ * allowing CommandHandler implementations to avoid unnecessary exception-handling. All checked
+ * exceptions are expected to be wrapped and handled by the caller.
+ *
+ * @param command - the Command to be handled
+ * @param session - the session on which the Command was submitted
+ *
+ * @throws Exception
+ * @throws AssertFailedException - if the command or session is null
+ *
+ * @see org.mockftpserver.core.command.CommandHandler#handleCommand(org.mockftpserver.core.command.Command,
+ * org.mockftpserver.core.session.Session)
+ */
+ public final void handleCommand(Command command, Session session) throws Exception {
+ Assert.notNull(command, "command");
+ Assert.notNull(session, "session");
+ InvocationRecord invocationRecord = new InvocationRecord(command, session.getClientHost());
+ invocations.add(invocationRecord);
+ handleCommand(command, session, invocationRecord);
+ invocationRecord.lock();
+ }
+
+ /**
+ * Handle the specified command for the session. This method is declared to throw Exception,
+ * allowing CommandHandler implementations to avoid unnecessary exception-handling. All checked
+ * exceptions are expected to be wrapped and handled by the caller.
+ *
+ * @param command - the Command to be handled
+ * @param session - the session on which the Command was submitted
+ * @param invocationRecord - the InvocationRecord; CommandHandlers are expected to add
+ * handler-specific data to the InvocationRecord, as appropriate
+ * @throws Exception
+ */
+ protected abstract void handleCommand(Command command, Session session, InvocationRecord invocationRecord)
+ throws Exception;
+
+ //-------------------------------------------------------------------------
+ // Support for reply text ResourceBundle
+ //-------------------------------------------------------------------------
+
+ /**
+ * Return the ResourceBundle containing the reply text messages
+ * @return the replyTextBundle
+ *
+ * @see org.mockftpserver.core.command.ReplyTextBundleAware#getReplyTextBundle()
+ */
+ public ResourceBundle getReplyTextBundle() {
+ return replyTextBundle;
+ }
+
+ /**
+ * Set the ResourceBundle containing the reply text messages
+ * @param replyTextBundle - the replyTextBundle to set
+ *
+ * @see org.mockftpserver.core.command.ReplyTextBundleAware#setReplyTextBundle(java.util.ResourceBundle)
+ */
+ public void setReplyTextBundle(ResourceBundle replyTextBundle) {
+ this.replyTextBundle = replyTextBundle;
+ }
+
+ // -------------------------------------------------------------------------
+ // Utility methods for subclasses
+ // -------------------------------------------------------------------------
+
+ /**
+ * Send a reply for this command on the control connection.
+ *
+ * The reply code is designated by the <code>replyCode</code> property, and the reply text
+ * is determined by the following rules:
+ * <ol>
+ * <li>If the <code>replyText</code> property is non-null, then use that.</li>
+ * <li>Otherwise, if <code>replyMessageKey</code> is non-null, the use that to retrieve a
+ * localized message from the <code>replyText</code> ResourceBundle.</li>
+ * <li>Otherwise, retrieve the reply text from the <code>replyText</code> ResourceBundle,
+ * using the reply code as the key.</li>
+ * </ol>
+ * If the arguments Object[] is not null, then these arguments are substituted within the
+ * reply text using the {@link MessageFormat} class.
+ *
+ * @param session - the Session
+ * @param replyCode - the reply code
+ * @param replyMessageKey - if not null (and replyText is null), this is used as the ResourceBundle
+ * message key instead of the reply code.
+ * @param replyText - if non-null, this is used as the reply text
+ * @param arguments - the array of arguments to be formatted and substituted within the reply
+ * text; may be null
+ *
+ * @throws AssertFailedException - if session is null
+ *
+ * @see MessageFormat
+ */
+ protected void sendReply(Session session, int replyCode, String replyMessageKey, String replyText,
+ Object[] arguments) {
+
+ Assert.notNull(session, "session");
+ assertValidReplyCode(replyCode);
+
+ final Logger HANDLER_LOG = Logger.getLogger(getClass());
+ String key = (replyMessageKey != null) ? replyMessageKey : Integer.toString(replyCode);
+ String text = getTextForReplyCode(replyCode, key, replyText, arguments);
+ String replyTextToLog = (text == null) ? "" : " " + text;
+ HANDLER_LOG.debug("Sending reply [" + replyCode + replyTextToLog + "]");
+ session.sendReply(replyCode, text);
+ }
+
+ /**
+ * Return the specified text surrounded with double quotes
+ *
+ * @param text - the text to surround with quotes
+ * @return the text with leading and trailing double quotes
+ *
+ * @throws AssertFailedException - if text is null
+ */
+ protected static String quotes(String text) {
+ Assert.notNull(text, "text");
+ final String QUOTES = "\"";
+ return QUOTES + text + QUOTES;
+ }
+
+ /**
+ * Assert that the specified number is a valid reply code
+ * @param replyCode - the reply code to check
+ *
+ * @throws AssertFailedException - if the replyCode is invalid
+ */
+ protected void assertValidReplyCode(int replyCode) {
+ Assert.isTrue(replyCode > 0, "The number [" + replyCode + "] is not a valid reply code");
+ }
+
+ // -------------------------------------------------------------------------
+ // InvocationHistory - Support for command history
+ // -------------------------------------------------------------------------
+
+ /**
+ * @return the number of invocation records stored for this command handler instance
+ *
+ * @see org.mockftpserver.core.command.InvocationHistory#numberOfInvocations()
+ */
+ public int numberOfInvocations() {
+ return invocations.size();
+ }
+
+ /**
+ * Return the InvocationRecord representing the command invoction data for the nth invocation
+ * for this command handler instance. One InvocationRecord should be stored for each invocation
+ * of the CommandHandler.
+ *
+ * @param index - the index of the invocation record to return. The first record is at index zero.
+ * @return the InvocationRecord for the specified index
+ *
+ * @throws AssertFailedException - if there is no invocation record corresponding to the specified index
+ *
+ * @see org.mockftpserver.core.command.InvocationHistory#getInvocation(int)
+ */
+ public InvocationRecord getInvocation(int index) {
+ return (InvocationRecord) invocations.get(index);
+ }
+
+ /**
+ * Clear out the invocation history for this CommandHandler. After invoking this method, the
+ * <code>numberOfInvocations()</code> method will return zero.
+ *
+ * @see org.mockftpserver.core.command.InvocationHistory#clearInvocations()
+ */
+ public void clearInvocations() {
+ invocations.clear();
+ }
+
+ // -------------------------------------------------------------------------
+ // Internal Helper Methods
+ // -------------------------------------------------------------------------
+
+ /**
+ * Return the text for the specified reply code, formatted using the message arguments, if
+ * supplied. If overrideText is not null, then return that. Otherwise, return the text mapped to
+ * the code from the replyText ResourceBundle. If the ResourceBundle contains no mapping, then
+ * return null.
+ * <p>
+ * If arguments is not null, then the returned reply text if formatted using the
+ * {@link MessageFormat} class.
+ *
+ * @param code - the reply code
+ * @param messageKey - the key used to retrieve the reply text from the replyTextBundle
+ * @param overrideText - if not null, this is used instead of the text from the replyTextBundle.
+ * @param arguments - the array of arguments to be formatted and substituted within the reply
+ * text; may be null
+ * @return the text for the reply code; may be null
+ */
+ private String getTextForReplyCode(int code, String messageKey, String overrideText, Object[] arguments) {
+ try {
+ String t = (overrideText == null) ? replyTextBundle.getString(messageKey) : overrideText;
+ String formattedMessage = MessageFormat.format(t, arguments);
+ return (formattedMessage == null) ? null : formattedMessage.trim();
+ }
+ catch (MissingResourceException e) {
+ // No reply text is mapped for the specified key
+ LOG.warn("No reply text defined for reply code [" + code + "]");
+ return null;
+ }
+ }
+
+}
diff --git a/tags/1.2.3/src/main/java/org/mockftpserver/core/command/Command.java b/tags/1.2.3/src/main/java/org/mockftpserver/core/command/Command.java
new file mode 100644
index 0000000..c0dd720
--- /dev/null
+++ b/tags/1.2.3/src/main/java/org/mockftpserver/core/command/Command.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.core.command;
+
+import java.util.Arrays;
+
+import org.mockftpserver.core.util.Assert;
+
+/**
+ * Represents a command received from an FTP client, containing a command name and parameters.
+ * Objects of this class are immutable.
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public final class Command {
+
+ private String name;
+ private String[] parameters;
+
+ /**
+ * Construct a new immutable instance with the specified command name and parameters
+ * @param name - the command name; may not be null
+ * @param parameters - the command parameters; may be empty; may not benull
+ */
+ public Command(String name, String[] parameters) {
+ Assert.notNull(name, "name");
+ Assert.notNull(parameters, "parameters");
+ this.name = name;
+ this.parameters = copy(parameters);
+ }
+
+ /**
+ * @return the name
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * @return the parameters
+ */
+ public String[] getParameters() {
+ return copy(parameters);
+ }
+
+ /**
+ * Get the String value of the parameter at the specified index
+ * @param index - the index
+ * @return the parameter value as a String
+ * @throws AssertFailedException if the parameter index is invalid or the value is not a valid String
+ */
+ public String getRequiredString(int index) {
+ assertValidIndex(index);
+ return parameters[index];
+ }
+
+ /**
+ * Get the String value of the parameter at the specified index; return null if no parameter exists for the index
+ * @param index - the index
+ * @return the parameter value as a String, or null if this Command does not have a parameter for that index
+ */
+ public String getOptionalString(int index) {
+ return (parameters.length > index) ? parameters[index] : null;
+ }
+
+ /**
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null || !(obj instanceof Command)) {
+ return false;
+ }
+ return this.hashCode() == obj.hashCode();
+ }
+
+ /**
+ * @see java.lang.Object#hashCode()
+ */
+ public int hashCode() {
+ String str = name + Arrays.asList(parameters);
+ return str.hashCode();
+ }
+
+ /**
+ * Return the String representation of this object
+ * @see java.lang.Object#toString()
+ */
+ public String toString() {
+ return "Command[" + name + ": " + Arrays.asList(parameters) + "]";
+ }
+
+ /**
+ * Return the name, normalized to a common format - convert to upper case.
+ * @return the name converted to upper case
+ */
+ public static String normalizeName(String name) {
+ return name.toUpperCase();
+ }
+
+ /**
+ * Construct a shallow copy of the specified array
+ * @param array - the array to copy
+ * @return a new array with the same contents
+ */
+ private static String[] copy(String[] array) {
+ String[] newArray = new String[array.length];
+ System.arraycopy(array, 0, newArray, 0, array.length);
+ return newArray;
+ }
+
+ /**
+ * Assert that the index is valid
+ * @param index - the index
+ * @throws AssertFailedException if the parameter index is invalid
+ */
+ private void assertValidIndex(int index) {
+ Assert.isTrue(index >= 0 && index < parameters.length, "The parameter index " + index + " is not valid for " + this);
+ }
+
+}
diff --git a/tags/1.2.3/src/main/java/org/mockftpserver/core/command/CommandHandler.java b/tags/1.2.3/src/main/java/org/mockftpserver/core/command/CommandHandler.java
new file mode 100644
index 0000000..e004b5b
--- /dev/null
+++ b/tags/1.2.3/src/main/java/org/mockftpserver/core/command/CommandHandler.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.core.command;
+
+import org.mockftpserver.core.session.Session;
+
+/**
+ * Interface for classes that can handle an FTP command.
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public interface CommandHandler {
+
+ /**
+ * Handle the specified command for the session. This method is declared to throw
+ * Exception, allowing CommandHandler implementations to avoid unnecessary
+ * exception-handling. All checked exceptions are expected to be wrapped and handled
+ * by the caller.
+ *
+ * @param command - the Command to be handled
+ * @param session - the session on which the Command was submitted
+ *
+ * @throws Exception
+ */
+ public void handleCommand(Command command, Session session) throws Exception;
+
+} \ No newline at end of file
diff --git a/tags/1.2.3/src/main/java/org/mockftpserver/core/command/CommandNames.java b/tags/1.2.3/src/main/java/org/mockftpserver/core/command/CommandNames.java
new file mode 100644
index 0000000..c56dd5b
--- /dev/null
+++ b/tags/1.2.3/src/main/java/org/mockftpserver/core/command/CommandNames.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.core.command;
+
+/**
+ * FTP command name constants.
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public final class CommandNames {
+
+ public static final String ABOR = "ABOR";
+ public static final String ACCT = "ACCT";
+ public static final String ALLO = "ALLO";
+ public static final String APPE = "APPE";
+ public static final String CDUP = "CDUP";
+ public static final String CONNECT = "CONNECT";
+ public static final String CWD = "CWD";
+ public static final String DELE = "DELE";
+ public static final String HELP = "HELP";
+ public static final String LIST = "LIST";
+ public static final String MKD = "MKD";
+ public static final String MODE = "MODE";
+ public static final String NLST = "NLST";
+ public static final String NOOP = "NOOP";
+ public static final String PASS = "PASS";
+ public static final String PASV = "PASV";
+ public static final String PORT = "PORT";
+ public static final String PWD = "PWD";
+ public static final String QUIT = "QUIT";
+ public static final String REIN = "REIN";
+ public static final String REST = "REST";
+ public static final String RETR = "RETR";
+ public static final String RMD = "RMD";
+ public static final String RNFR = "RNFR";
+ public static final String RNTO = "RNTO";
+ public static final String SITE = "SITE";
+ public static final String SMNT = "SMNT";
+ public static final String STAT = "STAT";
+ public static final String STOR = "STOR";
+ public static final String STOU = "STOU";
+ public static final String STRU = "STRU";
+ public static final String SYST = "SYST";
+ public static final String TYPE = "TYPE";
+ public static final String USER = "USER";
+
+ public static final String XPWD = "XPWD";
+
+ /**
+ * Private constructor. This class should not be instantiated.
+ */
+ private CommandNames() {
+ }
+
+}
diff --git a/tags/1.2.3/src/main/java/org/mockftpserver/core/command/InvocationHistory.java b/tags/1.2.3/src/main/java/org/mockftpserver/core/command/InvocationHistory.java
new file mode 100644
index 0000000..71c66c2
--- /dev/null
+++ b/tags/1.2.3/src/main/java/org/mockftpserver/core/command/InvocationHistory.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.core.command;
+
+/**
+ * Interface for an object that can retrieve and clear the history of InvocationRecords
+ * for a command handler.
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public interface InvocationHistory {
+
+ /**
+ * @return the number of invocation records stored for this command handler instance
+ */
+ public int numberOfInvocations();
+
+ /**
+ * Return the InvocationRecord representing the command invoction data for the nth invocation
+ * for this command handler instance. One InvocationRecord should be stored for each invocation
+ * of the CommandHandler.
+ *
+ * @param index - the index of the invocation record to return. The first record is at index zero.
+ * @return the InvocationRecord for the specified index
+ *
+ * @throws AssertFailedException - if there is no invocation record corresponding to the specified index */
+ public InvocationRecord getInvocation(int index);
+
+ /**
+ * Clear out the invocation history for this CommandHandler. After invoking this method, the
+ * <code>numberOfInvocations()</code> method will return zero.
+ */
+ public void clearInvocations();
+
+} \ No newline at end of file
diff --git a/tags/1.2.3/src/main/java/org/mockftpserver/core/command/InvocationRecord.java b/tags/1.2.3/src/main/java/org/mockftpserver/core/command/InvocationRecord.java
new file mode 100644
index 0000000..2e085b0
--- /dev/null
+++ b/tags/1.2.3/src/main/java/org/mockftpserver/core/command/InvocationRecord.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.core.command;
+
+import java.net.InetAddress;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import org.mockftpserver.core.util.Assert;
+import org.mockftpserver.core.util.AssertFailedException;
+
+/**
+ * Represents information about a single FTP Command invocation. Manages and provides access to
+ * the Command, the host address (<code>InetAddress</code>) of the client that submitted the
+ * Command and the timestamp of the Command submission.
+ * <p>
+ * This class also supports storing zero or more arbitrary mappings of <i>key</i> to value, where <i>key</i> is
+ * a String and <i>value</i> is any Object. Convenience methods are provided that enable retrieving
+ * type-specific data by its <i>key</i>. The data stored in an {@link InvocationRecord} is CommandHandler-specific.
+ * <p>
+ * The {@link #lock()} method makes an instance of this class immutable. After an instance is locked,
+ * calling the {@link #set(String, Object)} method will throw an <code>AssertFailedException</code>.
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public class InvocationRecord {
+
+ private Command command;
+ private Date time;
+ private InetAddress clientHost;
+ private Map data = new HashMap();
+ private boolean locked = false;
+
+ /**
+ * Create a new instance
+ * @param command - the Command
+ * @param clientHost - the client host
+ */
+ public InvocationRecord(Command command, InetAddress clientHost) {
+ this.command = command;
+ this.time = new Date();
+ this.clientHost = clientHost;
+ }
+
+ /**
+ * Lock this instance, making it immutable. After an instance is locked,
+ * calling the {@link #set(String, Object)} method will throw an
+ * <code>AssertFailedException</code>.
+ */
+ public void lock() {
+ locked = true;
+ }
+
+ /**
+ * Return true if this object has been locked, false otherwise. See {@link #lock()}.
+ * @return true if this object has been locked, false otherwise.
+ */
+ public boolean isLocked() {
+ return locked;
+ }
+
+ /**
+ * @return the client host that submitted the command, as an InetAddress
+ */
+ public InetAddress getClientHost() {
+ return clientHost;
+ }
+
+ /**
+ * @return the Command
+ */
+ public Command getCommand() {
+ return command;
+ }
+
+ /**
+ * @return the time that the command was processed; this may differ slightly from when the command was received.
+ */
+ public Date getTime() {
+ // Return a copy of the Date object to preserve immutability
+ return new Date(time.getTime());
+ }
+
+ /**
+ * Store the value for the specified key. If this object already contained a mapping
+ * for this key, the old value is replaced by the specified value. This method throws
+ * an <code>AssertFailedException</code> if this object has been locked. See {@link #lock()}.
+ *
+ * @param key - the key; must not be null
+ * @param value - the value to store for the specified key
+ *
+ * @throws AssertFailedException - if the key is null or this object has been locked.
+ */
+ public void set(String key, Object value) {
+ Assert.notNull(key, "key");
+ Assert.isFalse(locked, "The InvocationRecord is locked!");
+ data.put(key, value);
+ }
+
+ /**
+ * Returns <code>true</code> if this object contains a mapping for the specified key.
+ *
+ * @param key - the key; must not be null
+ * @return <code>true</code> if there is a mapping for the key
+ *
+ * @throws AssertFailedException - if the key is null
+ */
+ public boolean containsKey(String key) {
+ Assert.notNull(key, "key");
+ return data.containsKey(key);
+ }
+
+ /**
+ * Returns a Set view of the keys for the data stored in this object.
+ * Changes to the returned Set have no effect on the data stored within this object
+ * .
+ * @return the Set of keys for the data stored within this object
+ */
+ public Set keySet() {
+ return Collections.unmodifiableSet(data.keySet());
+ }
+
+ /**
+ * Get the String value associated with the specified key. Returns null if there is
+ * no mapping for this key. A return value of null does not necessarily indicate that
+ * this object contains no mapping for the key; it's also possible that the value was
+ * explicitly set to null for the key. The containsKey operation may be used to
+ * distinguish these two cases.
+ *
+ * @param key - the key; must not be null
+ * @return the String data stored at the specified key; may be null
+ *
+ * @throws ClassCastException - if the object for the specified key is not a String
+ * @throws AssertFailedException - if the key is null
+ */
+ public String getString(String key) {
+ Assert.notNull(key, "key");
+ return (String) data.get(key);
+ }
+
+ /**
+ * Get the Object value associated with the specified key. Returns null if there is
+ * no mapping for this key. A return value of null does not necessarily indicate that
+ * this object contains no mapping for the key; it's also possible that the value was
+ * explicitly set to null for the key. The containsKey operation may be used to
+ * distinguish these two cases.
+ *
+ * @param key - the key; must not be null
+ * @return the data stored at the specified key, as an Object; may be null
+ *
+ * @throws AssertFailedException - if the key is null
+ */
+ public Object getObject(String key) {
+ Assert.notNull(key, "key");
+ return data.get(key);
+ }
+
+ /**
+ * Return the String representation of this object
+ * @see java.lang.Object#toString()
+ */
+ public String toString() {
+ return "InvocationRecord[time=" + time + " client-host=" + clientHost + " command=" + command + " data="+ data + "]";
+ }
+}
diff --git a/tags/1.2.3/src/main/java/org/mockftpserver/core/command/ReplyCodes.java b/tags/1.2.3/src/main/java/org/mockftpserver/core/command/ReplyCodes.java
new file mode 100644
index 0000000..4bf03cf
--- /dev/null
+++ b/tags/1.2.3/src/main/java/org/mockftpserver/core/command/ReplyCodes.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.core.command;
+
+/**
+ * Reply Code constants.
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public final class ReplyCodes {
+
+ public static final int ABOR_OK = 226;
+ public static final int ACCT_OK = 230;
+ public static final int ALLO_OK = 200;
+ public static final int CDUP_OK = 250;
+ public static final int CWD_OK = 250;
+ public static final int DELE_OK = 250;
+ public static final int HELP_OK = 214;
+ public static final int MKD_OK = 257;
+ public static final int MODE_OK = 200;
+ public static final int NOOP_OK = 200;
+ public static final int PASS_OK = 230;
+ public static final int PASV_OK = 227;
+ public static final int PORT_OK = 200;
+ public static final int PWD_OK = 257;
+ public static final int QUIT_OK = 221;
+ public static final int REIN_OK = 220;
+ public static final int REST_OK = 350;
+ public static final int RMD_OK = 250;
+ public static final int RNFR_OK = 350;
+ public static final int RNTO_OK = 250;
+ public static final int SITE_OK = 200;
+ public static final int SMNT_OK = 250;
+ public static final int STAT_SYSTEM_OK = 211;
+ public static final int STAT_FILE_OK = 213;
+ public static final int STRU_OK = 200;
+ public static final int SYST_OK = 215;
+ public static final int TYPE_OK = 200;
+ public static final int USER_LOGGED_IN_OK = 230;
+ public static final int USER_NEED_PASSWORD_OK = 331;
+
+ public static final int SEND_DATA_INITIAL_OK = 150;
+ public static final int SEND_DATA_FINAL_OK = 226;
+
+ public static final int CONNECT_OK = 220;
+
+ /**
+ * Private constructor. This class should not be instantiated.
+ */
+ private ReplyCodes() {
+ }
+
+}
diff --git a/tags/1.2.3/src/main/java/org/mockftpserver/core/command/ReplyTextBundleAware.java b/tags/1.2.3/src/main/java/org/mockftpserver/core/command/ReplyTextBundleAware.java
new file mode 100644
index 0000000..42b5955
--- /dev/null
+++ b/tags/1.2.3/src/main/java/org/mockftpserver/core/command/ReplyTextBundleAware.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.core.command;
+
+import java.util.ResourceBundle;
+
+/**
+ * Interface for objects that allow getting and setting a reply text ResourceBundle. This
+ * interface is implemented by CommandHandlers so that the StubFtpServer can automatically
+ * set the default reply text ResourceBundle for the CommandHandler.
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public interface ReplyTextBundleAware {
+
+ /**
+ * Return the ResourceBundle containing the reply text messages
+ * @return the replyTextBundle
+ */
+ public ResourceBundle getReplyTextBundle();
+
+ /**
+ * Set the ResourceBundle containing the reply text messages
+ * @param replyTextBundle - the replyTextBundle to set
+ */
+ public void setReplyTextBundle(ResourceBundle replyTextBundle);
+
+} \ No newline at end of file
diff --git a/tags/1.2.3/src/main/java/org/mockftpserver/core/command/ReplyTextBundleUtil.java b/tags/1.2.3/src/main/java/org/mockftpserver/core/command/ReplyTextBundleUtil.java
new file mode 100644
index 0000000..e5d9fc0
--- /dev/null
+++ b/tags/1.2.3/src/main/java/org/mockftpserver/core/command/ReplyTextBundleUtil.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.core.command;
+
+import java.util.ResourceBundle;
+
+import org.mockftpserver.core.util.Assert;
+
+/**
+ * Contains common utility method to conditionally set the reply text ResourceBundle on a
+ * CommandHandler instance.
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public final class ReplyTextBundleUtil {
+
+ /**
+ * Set the <code>replyTextBundle</code> property of the specified CommandHandler to the
+ * <code>ResourceBundle</code> if and only if the <code>commandHandler</code> implements the
+ * {@link ReplyTextBundleAware} interface AND its <code>replyTextBundle</code> property
+ * has not been set (is null).
+ *
+ * @param commandHandler - the CommandHandler instance
+ * @param replyTextBundle - the ResourceBundle to use for localizing reply text
+ *
+ * @throws AssertFailedException - if the commandHandler is null
+ */
+ public static void setReplyTextBundleIfAppropriate(CommandHandler commandHandler, ResourceBundle replyTextBundle) {
+ Assert.notNull(commandHandler, "commandHandler");
+ if (commandHandler instanceof ReplyTextBundleAware) {
+ ReplyTextBundleAware replyTextBundleAware = (ReplyTextBundleAware) commandHandler;
+ if (replyTextBundleAware.getReplyTextBundle() == null) {
+ replyTextBundleAware.setReplyTextBundle(replyTextBundle);
+ }
+ }
+ }
+
+}
diff --git a/tags/1.2.3/src/main/java/org/mockftpserver/core/command/SimpleCompositeCommandHandler.java b/tags/1.2.3/src/main/java/org/mockftpserver/core/command/SimpleCompositeCommandHandler.java
new file mode 100644
index 0000000..a1e09ea
--- /dev/null
+++ b/tags/1.2.3/src/main/java/org/mockftpserver/core/command/SimpleCompositeCommandHandler.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.core.command;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ResourceBundle;
+
+import org.mockftpserver.core.session.Session;
+import org.mockftpserver.core.util.Assert;
+import org.mockftpserver.core.util.AssertFailedException;
+
+/**
+ * Composite CommandHandler that manages an internal list of CommandHandlers to which it delegates.
+ * The internal CommandHandlers are maintained in an ordered list. 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.
+ * <p>
+ * The following example replaces the CWD CommandHandler with a <code>SimpleCompositeCommandHandler</code>.
+ * The first invocation of the CWD command will fail (reply code 500). The seconds will succeed.
+ * <pre><code>
+ *
+ * StubFtpServer stubFtpServer = new StubFtpServer();
+ *
+ * CommandHandler commandHandler1 = new StaticReplyCommandHandler(500);
+ * CommandHandler commandHandler2 = new CwdCommandHandler();
+ *
+ * SimpleCompositeCommandHandler simpleCompositeCommandHandler = new SimpleCompositeCommandHandler();
+ * simpleCompositeCommandHandler.addCommandHandler(commandHandler1);
+ * simpleCompositeCommandHandler.addCommandHandler(commandHandler2);
+ *
+ * stubFtpServer.setCommandHandler("CWD", simpleCompositeCommandHandler);
+ * </code></pre>
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public final class SimpleCompositeCommandHandler implements CommandHandler, ReplyTextBundleAware {
+
+ private List commandHandlers = new ArrayList();
+ private int invocationIndex = 0;
+
+ /**
+ * Add a CommandHandler to the internal list of handlers.
+ *
+ * @param commandHandler - the CommandHandler
+ *
+ * @throws AssertFailedException - if the commandHandler is null
+ */
+ public void addCommandHandler(CommandHandler commandHandler) {
+ Assert.notNull(commandHandler, "commandHandler");
+ commandHandlers.add(commandHandler);
+ }
+
+ /**
+ * Set the List of CommandHandlers to which to delegate. This replaces any CommandHandlers that
+ * have been defined previously.
+ * @param commandHandlers - the complete List of CommandHandlers to which invocations are delegated
+ */
+ public void setCommandHandlers(List commandHandlers) {
+ Assert.notNull(commandHandlers, "commandHandlers");
+ this.commandHandlers = new ArrayList(commandHandlers);
+ }
+
+ /**
+ * Return the CommandHandler corresponding to the specified invocation index. In other words, return
+ * the CommandHandler instance to which the Nth {@link #handleCommand(Command, Session)} has been or will
+ * be delegated (where N=index).
+ * @param index - the index of the desired invocation (zero-based).
+ * @return the CommandHandler
+ *
+ * @throws AssertFailedException - if no CommandHandler is defined for the index or the index is not valid
+ */
+ public CommandHandler getCommandHandler(int index) {
+ Assert.isTrue(index < commandHandlers.size(), "No CommandHandler defined for index " + index);
+ Assert.isTrue(index >= 0, "The index cannot be less than zero: " + index);
+ return (CommandHandler) commandHandlers.get(index);
+ }
+
+ /**
+ * @see org.mockftpserver.core.command.CommandHandler#handleCommand(org.mockftpserver.core.command.Command, org.mockftpserver.core.session.Session)
+ */
+ public void handleCommand(Command command, Session session) throws Exception {
+ Assert.notNull(command, "command");
+ Assert.notNull(session, "session");
+ Assert.isTrue(commandHandlers.size() > invocationIndex, "No CommandHandler defined for invocation #" + invocationIndex);
+
+ CommandHandler commandHandler = (CommandHandler) commandHandlers.get(invocationIndex);
+ invocationIndex++;
+ commandHandler.handleCommand(command, session);
+ }
+
+ /**
+ * Returns null. This is a composite, and has no reply text bundle.
+ *
+ * @see org.mockftpserver.core.command.ReplyTextBundleAware#getReplyTextBundle()
+ */
+ public ResourceBundle getReplyTextBundle() {
+ return null;
+ }
+
+ /**
+ * Call <code>setReplyTextBundle()</code> on each of the command handlers within the internal list.
+ *
+ * @see org.mockftpserver.core.command.ReplyTextBundleAware#setReplyTextBundle(java.util.ResourceBundle)
+ */
+ public void setReplyTextBundle(ResourceBundle replyTextBundle) {
+ for (Iterator iter = commandHandlers.iterator(); iter.hasNext();) {
+ CommandHandler commandHandler = (CommandHandler) iter.next();
+ ReplyTextBundleUtil.setReplyTextBundleIfAppropriate(commandHandler, replyTextBundle);
+ }
+ }
+
+}
diff --git a/tags/1.2.3/src/main/java/org/mockftpserver/core/command/StaticReplyCommandHandler.java b/tags/1.2.3/src/main/java/org/mockftpserver/core/command/StaticReplyCommandHandler.java
new file mode 100644
index 0000000..b75aab6
--- /dev/null
+++ b/tags/1.2.3/src/main/java/org/mockftpserver/core/command/StaticReplyCommandHandler.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.core.command;
+
+import org.mockftpserver.core.session.Session;
+import org.mockftpserver.core.util.AssertFailedException;
+import org.mockftpserver.stub.command.AbstractStubCommandHandler;
+
+/**
+ * CommandHandler that sends back the configured reply code and text. You can customize the
+ * returned reply code by setting the required <code>replyCode</code> property. If only the
+ * <code>replyCode</code> property is set, then the default reply text corresponding to that
+ * reply code is used in the response. You can optionally configure the reply text by setting
+ * the <code>replyMessageKey</code> or <code>replyText</code> property.
+ * <p>
+ * Each invocation record stored by this CommandHandler contains no data elements.
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public final class StaticReplyCommandHandler extends AbstractStubCommandHandler implements CommandHandler {
+
+ /**
+ * Create a new uninitialized instance
+ */
+ public StaticReplyCommandHandler() {
+ }
+
+ /**
+ * Create a new instance with the specified replyCode
+ * @param replyCode - the replyCode to use
+ * @throws AssertFailedException - if the replyCode is null
+ */
+ public StaticReplyCommandHandler(int replyCode) {
+ setReplyCode(replyCode);
+ }
+
+ /**
+ * Create a new instance with the specified replyCode and replyText
+ * @param replyCode - the replyCode to use
+ * @param replyText - the replyText
+ * @throws AssertFailedException - if the replyCode is null
+ */
+ public StaticReplyCommandHandler(int replyCode, String replyText) {
+ setReplyCode(replyCode);
+ setReplyText(replyText);
+ }
+
+ /**
+ * @see org.mockftpserver.core.command.CommandHandler#handleCommand(Command, Session, InvocationRecord)
+ */
+ public void handleCommand(Command command, Session session, InvocationRecord invocationRecord) {
+ sendReply(session);
+ }
+
+}
diff --git a/tags/1.2.3/src/main/java/org/mockftpserver/core/session/DefaultSession.java b/tags/1.2.3/src/main/java/org/mockftpserver/core/session/DefaultSession.java
new file mode 100644
index 0000000..ce45f3b
--- /dev/null
+++ b/tags/1.2.3/src/main/java/org/mockftpserver/core/session/DefaultSession.java
@@ -0,0 +1,476 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.core.session;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.io.Writer;
+import java.net.InetAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.SocketTimeoutException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.StringTokenizer;
+
+import org.apache.log4j.Logger;
+import org.mockftpserver.core.MockFtpServerException;
+import org.mockftpserver.core.command.Command;
+import org.mockftpserver.core.command.CommandHandler;
+import org.mockftpserver.core.command.CommandNames;
+import org.mockftpserver.core.socket.DefaultServerSocketFactory;
+import org.mockftpserver.core.socket.DefaultSocketFactory;
+import org.mockftpserver.core.socket.ServerSocketFactory;
+import org.mockftpserver.core.socket.SocketFactory;
+import org.mockftpserver.core.util.Assert;
+import org.mockftpserver.core.util.AssertFailedException;
+
+/**
+ * Default implementation of the {@link Session} interface.
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public class DefaultSession implements Session {
+
+ private static final Logger LOG = Logger.getLogger(DefaultSession.class);
+ static final int DEFAULT_CLIENT_DATA_PORT = 21;
+
+ SocketFactory socketFactory = new DefaultSocketFactory();
+ ServerSocketFactory serverSocketFactory = new DefaultServerSocketFactory();
+
+ private BufferedReader controlConnectionReader;
+ private Writer controlConnectionWriter;
+ private Socket controlSocket;
+ private Socket dataSocket;
+ ServerSocket passiveModeDataSocket; // non-private for testing
+ private InputStream dataInputStream;
+ private OutputStream dataOutputStream;
+ private Map commandHandlers;
+ private int clientDataPort = DEFAULT_CLIENT_DATA_PORT;
+ private InetAddress clientHost;
+ private InetAddress serverHost;
+ private Map attributes = new HashMap();
+ private volatile boolean terminate = false;
+
+ /**
+ * Create a new initialized instance
+ *
+ * @param controlSocket - the control connection socket
+ * @param commandHandlers - the Map of command name -> CommandHandler. It is assumed that the
+ * command names are all normalized to upper case. See {@link Command#normalizeName(String)}.
+ */
+ public DefaultSession(Socket controlSocket, Map commandHandlers) {
+ Assert.notNull(controlSocket, "controlSocket");
+ Assert.notNull(commandHandlers, "commandHandlers");
+
+ this.controlSocket = controlSocket;
+ this.commandHandlers = commandHandlers;
+ this.serverHost = controlSocket.getLocalAddress();
+ }
+
+ /**
+ * Return the InetAddress representing the client host for this session
+ *
+ * @return the client host
+ *
+ * @see org.mockftpserver.core.session.Session#getClientHost()
+ */
+ public InetAddress getClientHost() {
+ return controlSocket.getInetAddress();
+ }
+
+ /**
+ * Return the InetAddress representing the server host for this session
+ *
+ * @return the server host
+ *
+ * @see org.mockftpserver.core.session.Session#getServerHost()
+ */
+ public InetAddress getServerHost() {
+ return serverHost;
+ }
+
+ /**
+ * Send the specified reply code and text across the control connection.
+ * The reply text is trimmed before being sent.
+ *
+ * @param code - the reply code
+ * @param text - the reply text to send; may be null
+ */
+ public void sendReply(int code, String text) {
+ assertValidReplyCode(code);
+
+ StringBuffer buffer = new StringBuffer(Integer.toString(code));
+
+ if (text != null && text.length() > 0) {
+ String replyText = text.trim();
+ if (replyText.indexOf("\n") != -1) {
+ int lastIndex = replyText.lastIndexOf("\n");
+ buffer.append("-");
+ for (int i = 0; i < replyText.length(); i++) {
+ char c = replyText.charAt(i);
+ buffer.append(c);
+ if (i == lastIndex) {
+ buffer.append(Integer.toString(code) + " ");
+ }
+ }
+ }
+ else {
+ buffer.append(" ");
+ buffer.append(replyText);
+ }
+ }
+ LOG.debug("Sending Reply [" + buffer.toString() + "]");
+ writeLineToControlConnection(buffer.toString());
+ }
+
+ /**
+ * @see org.mockftpserver.core.session.Session#openDataConnection()
+ */
+ public void openDataConnection() {
+ try {
+ if (passiveModeDataSocket != null) {
+ LOG.debug("Waiting for (passive mode) client connection from client host [" + clientHost
+ + "] on port " + passiveModeDataSocket.getLocalPort());
+ // TODO set socket timeout
+ try {
+ dataSocket = passiveModeDataSocket.accept();
+ LOG.debug("Successful (passive mode) client connection to port "
+ + passiveModeDataSocket.getLocalPort());
+ }
+ catch (SocketTimeoutException e) {
+ throw new MockFtpServerException(e);
+ }
+ }
+ else {
+ Assert.notNull(clientHost, "clientHost");
+ LOG.debug("Connecting to client host [" + clientHost + "] on data port [" + clientDataPort
+ + "]");
+ dataSocket = socketFactory.createSocket(clientHost, clientDataPort);
+ }
+ dataOutputStream = dataSocket.getOutputStream();
+ dataInputStream = dataSocket.getInputStream();
+ }
+ catch (IOException e) {
+ throw new MockFtpServerException(e);
+ }
+ }
+
+ /**
+ * Switch to passive mode
+ *
+ * @return the local port to be connected to by clients for data transfers
+ *
+ * @see org.mockftpserver.core.session.Session#switchToPassiveMode()
+ */
+ public int switchToPassiveMode() {
+ try {
+ passiveModeDataSocket = serverSocketFactory.createServerSocket(0);
+ return passiveModeDataSocket.getLocalPort();
+ }
+ catch (IOException e) {
+ throw new MockFtpServerException("Error opening passive mode server data socket", e);
+ }
+ }
+
+ /**
+ * @see org.mockftpserver.core.session.Session#closeDataConnection()
+ */
+ public void closeDataConnection() {
+ try {
+ LOG.debug("Flushing and closing client data socket");
+ dataOutputStream.flush();
+ dataOutputStream.close();
+ dataInputStream.close();
+ dataSocket.close();
+ }
+ catch (IOException e) {
+ LOG.error("Error closing client data socket", e);
+ }
+ }
+
+ /**
+ * Write a single line to the control connection, appending a newline
+ *
+ * @param line - the line to write
+ */
+ private void writeLineToControlConnection(String line) {
+ try {
+ controlConnectionWriter.write(line + "\n");
+ controlConnectionWriter.flush();
+ }
+ catch (IOException e) {
+ LOG.error("Error writing to control connection", e);
+ throw new MockFtpServerException("Error writing to control connection", e);
+ }
+ }
+
+ /**
+ * @see org.mockftpserver.core.session.Session#close()
+ */
+ public void close() {
+ LOG.trace("close()");
+ terminate = true;
+ }
+
+ /**
+ * @see org.mockftpserver.core.session.Session#sendData(byte[], int)
+ */
+ public void sendData(byte[] data, int numBytes) {
+ Assert.notNull(data, "data");
+ try {
+ dataOutputStream.write(data, 0, numBytes);
+ }
+ catch (IOException e) {
+ throw new MockFtpServerException(e);
+ }
+ }
+
+ /**
+ * @see org.mockftpserver.core.session.Session#readData()
+ */
+ public byte[] readData() {
+
+ ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+
+ try {
+ while (true) {
+ int b = dataInputStream.read();
+ if (b == -1) {
+ break;
+ }
+ bytes.write(b);
+ }
+ return bytes.toByteArray();
+ }
+ catch (IOException e) {
+ throw new MockFtpServerException(e);
+ }
+ }
+
+ /**
+ * Wait for and read the command sent from the client on the control connection.
+ *
+ * @return the Command sent from the client; may be null if the session has been closed
+ *
+ * Package-private to enable testing
+ */
+ Command readCommand() {
+
+ final long socketReadIntervalMilliseconds = 100L;
+
+ try {
+ while (true) {
+ if (terminate) {
+ return null;
+ }
+ // Don't block; only read command when it is available
+ if (controlConnectionReader.ready()) {
+ String command = controlConnectionReader.readLine();
+ LOG.info("Received command: [" + command + "]");
+ return parseCommand(command);
+ }
+ try {
+ Thread.sleep(socketReadIntervalMilliseconds);
+ }
+ catch (InterruptedException e) {
+ throw new MockFtpServerException(e);
+ }
+ }
+ }
+ catch (IOException e) {
+ LOG.error("Read failed", e);
+ throw new MockFtpServerException(e);
+ }
+ }
+
+ /**
+ * Parse the command String into a Command object
+ *
+ * @param commandString - the command String
+ * @return the Command object parsed from the command String
+ */
+ Command parseCommand(String commandString) {
+ Assert.notNullOrEmpty(commandString, "commandString");
+
+ List parameters = new ArrayList();
+ String name;
+
+ int indexOfFirstSpace = commandString.indexOf(" ");
+ if (indexOfFirstSpace != -1) {
+ name = commandString.substring(0, indexOfFirstSpace);
+ StringTokenizer tokenizer = new StringTokenizer(commandString.substring(indexOfFirstSpace + 1),
+ ",");
+ while (tokenizer.hasMoreTokens()) {
+ parameters.add(tokenizer.nextToken());
+ }
+ }
+ else {
+ name = commandString;
+ }
+
+ String[] parametersArray = new String[parameters.size()];
+ return new Command(name, (String[]) parameters.toArray(parametersArray));
+ }
+
+ /**
+ * @see org.mockftpserver.core.session.Session#setClientDataHost(java.net.InetAddress)
+ */
+ public void setClientDataHost(InetAddress clientHost) {
+ this.clientHost = clientHost;
+ }
+
+ /**
+ * @see org.mockftpserver.core.session.Session#setClientDataPort(int)
+ */
+ public void setClientDataPort(int dataPort) {
+ this.clientDataPort = dataPort;
+
+ // Clear out any passive data connection mode information
+ if (passiveModeDataSocket != null) {
+ try {
+ this.passiveModeDataSocket.close();
+ }
+ catch (IOException e) {
+ throw new MockFtpServerException(e);
+ }
+ passiveModeDataSocket = null;
+ }
+ }
+
+ /**
+ * @see java.lang.Runnable#run()
+ */
+ public void run() {
+ try {
+
+ InputStream inputStream = controlSocket.getInputStream();
+ OutputStream outputStream = controlSocket.getOutputStream();
+ controlConnectionReader = new BufferedReader(new InputStreamReader(inputStream));
+ controlConnectionWriter = new PrintWriter(outputStream, true);
+
+ LOG.debug("Starting the session...");
+
+ CommandHandler connectCommandHandler = (CommandHandler) commandHandlers.get(CommandNames.CONNECT);
+ connectCommandHandler.handleCommand(new Command(CommandNames.CONNECT, new String[0]), this);
+
+ while (!terminate) {
+ readAndProcessCommand();
+ }
+ }
+ catch (Exception e) {
+ LOG.error(e);
+ throw new MockFtpServerException(e);
+ }
+ finally {
+ LOG.debug("Cleaning up the session");
+ try {
+ controlConnectionReader.close();
+ controlConnectionWriter.close();
+ }
+ catch (IOException e) {
+ LOG.error(e);
+ throw new MockFtpServerException(e);
+ }
+ LOG.debug("Session stopped.");
+ }
+ }
+
+ /**
+ * Read and process the next command from the control connection
+ *
+ * @throws Exception
+ */
+ private void readAndProcessCommand() throws Exception {
+
+ Command command = readCommand();
+ if (command != null) {
+ String normalizedCommandName = Command.normalizeName(command.getName());
+ CommandHandler commandHandler = (CommandHandler) commandHandlers.get(normalizedCommandName);
+ Assert.notNull(commandHandler, "CommandHandler for command [" + normalizedCommandName + "]");
+ commandHandler.handleCommand(command, this);
+ }
+ }
+
+ /**
+ * Assert that the specified number is a valid reply code
+ *
+ * @param replyCode - the reply code to check
+ */
+ private void assertValidReplyCode(int replyCode) {
+ Assert.isTrue(replyCode > 0, "The number [" + replyCode + "] is not a valid reply code");
+ }
+
+ /**
+ * Return the attribute value for the specified name. Return null if no attribute value
+ * exists for that name or if the attribute value is null.
+ * @param name - the attribute name; may not be null
+ * @return the value of the attribute stored under name; may be null
+ *
+ * @see org.mockftpserver.core.session.Session#getAttribute(java.lang.String)
+ */
+ public Object getAttribute(String name) {
+ Assert.notNull(name, "name");
+ return attributes.get(name);
+ }
+
+ /**
+ * Store the value under the specified attribute name.
+ * @param name - the attribute name; may not be null
+ * @param value - the attribute value; may be null
+ *
+ * @see org.mockftpserver.core.session.Session#setAttribute(java.lang.String, java.lang.Object)
+ */
+ public void setAttribute(String name, Object value) {
+ Assert.notNull(name, "name");
+ attributes.put(name, value);
+ }
+
+ /**
+ * Return the Set of names under which attributes have been stored on this session.
+ * Returns an empty Set if no attribute values are stored.
+ * @return the Set of attribute names
+ *
+ * @see org.mockftpserver.core.session.Session#getAttributeNames()
+ */
+ public Set getAttributeNames() {
+ return attributes.keySet();
+ }
+
+ /**
+ * Remove the attribute value for the specified name. Do nothing if no attribute
+ * value is stored for the specified name.
+ * @param name - the attribute name; may not be null
+ * @throws AssertFailedException - if name is null
+ *
+ * @see org.mockftpserver.core.session.Session#removeAttribute(java.lang.String)
+ */
+ public void removeAttribute(String name) {
+ Assert.notNull(name, "name");
+ attributes.remove(name);
+ }
+
+}
diff --git a/tags/1.2.3/src/main/java/org/mockftpserver/core/session/Session.java b/tags/1.2.3/src/main/java/org/mockftpserver/core/session/Session.java
new file mode 100644
index 0000000..d17c265
--- /dev/null
+++ b/tags/1.2.3/src/main/java/org/mockftpserver/core/session/Session.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.core.session;
+
+import java.net.InetAddress;
+import java.util.Set;
+
+/**
+ * Represents an FTP session state and behavior
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public interface Session extends Runnable {
+
+ /**
+ * Close the session, closing the underlying sockets
+ */
+ public void close();
+
+ /**
+ * Send the specified reply code and text across the control connection.
+ *
+ * @param replyCode - the reply code
+ * @param replyText - the reply text to send; may be null
+ */
+ public void sendReply(int replyCode, String replyText);
+
+ /**
+ * Open the data connection, attaching to the predefined port number on the client
+ */
+ public void openDataConnection();
+
+ /**
+ * Close the data connection
+ */
+ public void closeDataConnection();
+
+ /**
+ * Switch to passive mode
+ * @return the local port to be connected to by clients for data transfers
+ */
+ public int switchToPassiveMode();
+
+ /**
+ * Write the specified data using the data connection
+ *
+ * @param data - the data to write
+ * @param numBytes - the number of bytes from data to send
+ */
+ public void sendData(byte[] data, int numBytes);
+
+ /**
+ * Read data from the client across the data connection
+ *
+ * @return the data that was read
+ */
+ public byte[] readData();
+
+ /**
+ * Return the InetAddress representing the client host for this session
+ * @return the client host
+ */
+ public InetAddress getClientHost();
+
+ /**
+ * Return the InetAddress representing the server host for this session
+ * @return the server host
+ */
+ public InetAddress getServerHost();
+
+ /**
+ * @param clientHost - the client host for the data connection
+ */
+ public void setClientDataHost(InetAddress clientHost);
+
+ /**
+ * @param clientDataPort - the port number on the client side for the data connection
+ */
+ public void setClientDataPort(int clientDataPort);
+
+ /**
+ * Return the attribute value for the specified name. Return null if no attribute value
+ * exists for that name or if the attribute value is null.
+ * @param name - the attribute name; may not be null
+ * @return the value of the attribute stored under name; may be null
+ * @throws AssertFailedException - if name is null
+ */
+ public Object getAttribute(String name);
+
+ /**
+ * Store the value under the specified attribute name.
+ * @param name - the attribute name; may not be null
+ * @param value - the attribute value; may be null
+ * @throws AssertFailedException - if name is null
+ */
+ public void setAttribute(String name, Object value);
+
+ /**
+ * Remove the attribute value for the specified name. Do nothing if no attribute
+ * value is stored for the specified name.
+ * @param name - the attribute name; may not be null
+ * @throws AssertFailedException - if name is null
+ */
+ public void removeAttribute(String name);
+
+ /**
+ * Return the Set of names under which attributes have been stored on this session.
+ * Returns an empty Set if no attribute values are stored.
+ * @return the Set of attribute names
+ */
+ public Set getAttributeNames();
+
+} \ No newline at end of file
diff --git a/tags/1.2.3/src/main/java/org/mockftpserver/core/socket/DefaultServerSocketFactory.java b/tags/1.2.3/src/main/java/org/mockftpserver/core/socket/DefaultServerSocketFactory.java
new file mode 100644
index 0000000..ac6cd0f
--- /dev/null
+++ b/tags/1.2.3/src/main/java/org/mockftpserver/core/socket/DefaultServerSocketFactory.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.core.socket;
+
+import java.io.IOException;
+import java.net.ServerSocket;
+
+/**
+ * Default implementation of the {@link ServerSocketFactory}; creates standard {@link ServerSocket} instances.
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public class DefaultServerSocketFactory implements ServerSocketFactory {
+
+ /**
+ * Create a new ServerSocket for the specified port.
+ * @param port - the port
+ * @return a new ServerSocket
+ * @throws IOException
+
+ * @see org.mockftpserver.core.socket.ServerSocketFactory#createServerSocket(int)
+ */
+ public ServerSocket createServerSocket(int port) throws IOException {
+ return new ServerSocket(port);
+ }
+} \ No newline at end of file
diff --git a/tags/1.2.3/src/main/java/org/mockftpserver/core/socket/DefaultSocketFactory.java b/tags/1.2.3/src/main/java/org/mockftpserver/core/socket/DefaultSocketFactory.java
new file mode 100644
index 0000000..0a365e7
--- /dev/null
+++ b/tags/1.2.3/src/main/java/org/mockftpserver/core/socket/DefaultSocketFactory.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.core.socket;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.Socket;
+
+/**
+ * Default implementation of the {@link SocketFactory}; creates standard {@link Socket} instances.
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public class DefaultSocketFactory implements SocketFactory {
+
+ /**
+ * Create a new Socket instance for the specified host and port.
+ * @param host - the IP address of the host endpoint to which the socket is connect
+ * @param port - the port number of the enpoint to which the socket is connected
+ * @return a new Socket
+ * @throws IOException
+ *
+ * @see org.mockftpserver.core.socket.SocketFactory#createSocket(java.net.InetAddress, int)
+ */
+ public Socket createSocket(InetAddress host, int port) throws IOException {
+ return new Socket(host, port);
+ }
+} \ No newline at end of file
diff --git a/tags/1.2.3/src/main/java/org/mockftpserver/core/socket/ServerSocketFactory.java b/tags/1.2.3/src/main/java/org/mockftpserver/core/socket/ServerSocketFactory.java
new file mode 100644
index 0000000..af810a1
--- /dev/null
+++ b/tags/1.2.3/src/main/java/org/mockftpserver/core/socket/ServerSocketFactory.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.core.socket;
+
+import java.io.IOException;
+import java.net.ServerSocket;
+
+/**
+ * Interface for factory that creates new {@link ServerSocket} instances.
+ * Using this abstraction enables unit testing.
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public interface ServerSocketFactory {
+
+ /**
+ * Create a new ServerSocket for the specified port
+ * @param port - the port
+ * @return a new ServerSocket
+ * @throws IOException
+ */
+ public ServerSocket createServerSocket(int port) throws IOException;
+} \ No newline at end of file
diff --git a/tags/1.2.3/src/main/java/org/mockftpserver/core/socket/SocketFactory.java b/tags/1.2.3/src/main/java/org/mockftpserver/core/socket/SocketFactory.java
new file mode 100644
index 0000000..57d9778
--- /dev/null
+++ b/tags/1.2.3/src/main/java/org/mockftpserver/core/socket/SocketFactory.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.core.socket;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.Socket;
+
+/**
+ * Interface for factory that create new {@link Socket} instances.
+ * Using this abstraction enables unit testing.
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public interface SocketFactory {
+
+ /**
+ * Create a new Socket instance for the specified host and port.
+ * @param host - the IP address of the host endpoint to which the socket is connect
+ * @param port - the port number of the enpoint to which the socket is connected
+ * @return a new Socket
+ * @throws IOException
+ */
+ public Socket createSocket(InetAddress host, int port) throws IOException;
+} \ No newline at end of file
diff --git a/tags/1.2.3/src/main/java/org/mockftpserver/core/util/Assert.java b/tags/1.2.3/src/main/java/org/mockftpserver/core/util/Assert.java
new file mode 100644
index 0000000..de4a8cd
--- /dev/null
+++ b/tags/1.2.3/src/main/java/org/mockftpserver/core/util/Assert.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.core.util;
+
+import java.util.Collection;
+import java.util.Map;
+
+/**
+ * Provides static helper methods to make runtime assertions. Throws an
+ * <code>AssertFailedException</code> when the assertion fails. All methods are static.
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public final class Assert {
+
+ /**
+ * Verify that arg is null. Throw an AssertFailedException if it is not null.
+ * @param arg - the method parameter value
+ * @param argName - the name of the parameter; used in the exception message
+ * @throws AssertFailedException - if arg is not null
+ */
+ public static void isNull(Object arg, String argName) {
+ if (arg != null) {
+ throw new AssertFailedException("The value for \"" + argName + "\" must be null");
+ }
+ }
+
+ /**
+ * Verify that arg is not null. Throw an AssertFailedException if it is null.
+ * @param arg - the method parameter value
+ * @param argName - the name of the parameter; used in the exception message
+ * @throws AssertFailedException - if arg is null
+ */
+ public static void notNull(Object arg, String argName) {
+ if (arg == null) {
+ throw new AssertFailedException("The value of \"" + argName + "\" is null");
+ }
+ }
+
+ /**
+ * Verify that condition is true. Throw an AssertFailedException if it is false.
+ * @param condition - the condition that should be true
+ * @throws AssertFailedException - if condition is false
+ */
+ public static void isTrue(boolean condition, String message) {
+ if (!condition) {
+ throw new AssertFailedException(message);
+ }
+ }
+
+ /**
+ * Verify that condition is false. Throw an AssertFailedException if it is true.
+ * @param condition - the condition that should be false
+ * @throws AssertFailedException - if condition is true
+ */
+ public static void isFalse(boolean condition, String message) {
+ if (condition) {
+ throw new AssertFailedException(message);
+ }
+ }
+
+ /**
+ * Verify that the collection is not null or empty. Throw an
+ * AssertFailedException if it is null or empty.
+ * @param collection - the Collection
+ * @param argName - the name of the parameter; used in the exception message
+ * @throws AssertFailedException - if collection is null or empty
+ */
+ public static void notNullOrEmpty(Collection collection, String argName) {
+ notNull(collection, argName);
+ if (collection.isEmpty()) {
+ throw new AssertFailedException("The \"" + argName + "\" Collection is empty");
+ }
+ }
+
+ /**
+ * Verify that the Map is not null or empty. Throw an AssertFailedException
+ * if it is null or empty.
+ * @param map - the Map
+ * @param argName - the name of the parameter; used in the exception message
+ * @throws AssertFailedException - if map is null or empty
+ */
+ public static void notNullOrEmpty(Map map, String argName) {
+ notNull(map, argName);
+ if (map.isEmpty()) {
+ throw new AssertFailedException("The \"" + argName + "\" Map is empty");
+ }
+ }
+
+ /**
+ * Verify that the array is not null or empty. Throw an
+ * AssertFailedException if it is null or empty.
+ * @param array - the array
+ * @param argName - the name of the parameter; used in the exception message
+ * @throws AssertFailedException - if array is null or empty
+ */
+ public static void notNullOrEmpty(Object[] array, String argName) {
+ notNull(array, argName);
+ if (array.length == 0) {
+ throw new AssertFailedException("The \"" + argName + "\" array is empty");
+ }
+ }
+
+ /**
+ * Verify that the String is not null or empty. Throw an
+ * AssertFailedException if it is null or empty.
+ * @param string - the String
+ * @param argName - the name of the parameter; used in the exception message
+ * @throws AssertFailedException - if string is null or empty
+ */
+ public static void notNullOrEmpty(String string, String argName) {
+ notNull(string, argName);
+ if (string.trim().length() == 0) {
+ throw new AssertFailedException("The \"" + argName + "\" String is empty");
+ }
+ }
+
+ /**
+ * Private constructor. All methods are static
+ */
+ private Assert() {
+ }
+}
diff --git a/tags/1.2.3/src/main/java/org/mockftpserver/core/util/AssertFailedException.java b/tags/1.2.3/src/main/java/org/mockftpserver/core/util/AssertFailedException.java
new file mode 100644
index 0000000..a0b190e
--- /dev/null
+++ b/tags/1.2.3/src/main/java/org/mockftpserver/core/util/AssertFailedException.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.core.util;
+
+/**
+ * Exception that indicates that a runtime assertion from the
+ * {@link org.mockftpserver.core.util.Assert} has failed.
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public final class AssertFailedException extends RuntimeException {
+
+ /**
+ * Create a new instance for the specified message
+ * @param message - the exception message
+ */
+ public AssertFailedException(final String message) {
+ super(message);
+ }
+
+}
diff --git a/tags/1.2.3/src/main/java/org/mockftpserver/stub/StubFtpServer.java b/tags/1.2.3/src/main/java/org/mockftpserver/stub/StubFtpServer.java
new file mode 100644
index 0000000..d297326
--- /dev/null
+++ b/tags/1.2.3/src/main/java/org/mockftpserver/stub/StubFtpServer.java
@@ -0,0 +1,403 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.stub;
+
+import java.io.IOException;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.SocketTimeoutException;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.ResourceBundle;
+
+import org.apache.log4j.Logger;
+import org.mockftpserver.core.MockFtpServerException;
+import org.mockftpserver.core.command.Command;
+import org.mockftpserver.core.command.CommandHandler;
+import org.mockftpserver.core.command.CommandNames;
+import org.mockftpserver.core.command.ReplyTextBundleAware;
+import org.mockftpserver.core.command.ReplyTextBundleUtil;
+import org.mockftpserver.core.session.DefaultSession;
+import org.mockftpserver.core.session.Session;
+import org.mockftpserver.core.socket.DefaultServerSocketFactory;
+import org.mockftpserver.core.socket.ServerSocketFactory;
+import org.mockftpserver.core.util.Assert;
+import org.mockftpserver.core.util.AssertFailedException;
+import org.mockftpserver.stub.command.AborCommandHandler;
+import org.mockftpserver.stub.command.AcctCommandHandler;
+import org.mockftpserver.stub.command.AlloCommandHandler;
+import org.mockftpserver.stub.command.AppeCommandHandler;
+import org.mockftpserver.stub.command.CdupCommandHandler;
+import org.mockftpserver.stub.command.ConnectCommandHandler;
+import org.mockftpserver.stub.command.CwdCommandHandler;
+import org.mockftpserver.stub.command.DeleCommandHandler;
+import org.mockftpserver.stub.command.HelpCommandHandler;
+import org.mockftpserver.stub.command.ListCommandHandler;
+import org.mockftpserver.stub.command.MkdCommandHandler;
+import org.mockftpserver.stub.command.ModeCommandHandler;
+import org.mockftpserver.stub.command.NlstCommandHandler;
+import org.mockftpserver.stub.command.NoopCommandHandler;
+import org.mockftpserver.stub.command.PassCommandHandler;
+import org.mockftpserver.stub.command.PasvCommandHandler;
+import org.mockftpserver.stub.command.PortCommandHandler;
+import org.mockftpserver.stub.command.PwdCommandHandler;
+import org.mockftpserver.stub.command.QuitCommandHandler;
+import org.mockftpserver.stub.command.ReinCommandHandler;
+import org.mockftpserver.stub.command.RestCommandHandler;
+import org.mockftpserver.stub.command.RetrCommandHandler;
+import org.mockftpserver.stub.command.RmdCommandHandler;
+import org.mockftpserver.stub.command.RnfrCommandHandler;
+import org.mockftpserver.stub.command.RntoCommandHandler;
+import org.mockftpserver.stub.command.SiteCommandHandler;
+import org.mockftpserver.stub.command.SmntCommandHandler;
+import org.mockftpserver.stub.command.StatCommandHandler;
+import org.mockftpserver.stub.command.StorCommandHandler;
+import org.mockftpserver.stub.command.StouCommandHandler;
+import org.mockftpserver.stub.command.StruCommandHandler;
+import org.mockftpserver.stub.command.SystCommandHandler;
+import org.mockftpserver.stub.command.TypeCommandHandler;
+import org.mockftpserver.stub.command.UserCommandHandler;
+
+/**
+ * <b>StubFtpServer</b> is the top-level class for a "stub" implementation of an FTP Server,
+ * suitable for testing FTP client code or standing in for a live FTP server. It supports
+ * the main FTP commands by defining handlers for each of the corresponding low-level FTP
+ * server commands (e.g. RETR, DELE, LIST). These handlers implement the {@link CommandHandler}
+ * interface.
+ * <p>
+ * <b>StubFtpServer</b> works out of the box with default command handlers that return
+ * success reply codes and empty data (for retrieved files, directory listings, etc.).
+ * The command handler for any command can be easily configured to return custom data
+ * or reply codes. Or it can be replaced with a custom {@link CommandHandler}
+ * implementation. This allows simulation of a complete range of both success and
+ * failure scenarios. The command handlers can also be interrogated to verify command
+ * invocation data such as command parameters and timestamps.
+ * <p>
+ * <b>StubFtpServer</b> can be fully configured programmatically or within a Spring Framework
+ * ({@link http://www.springframework.org/}) or similar container.
+ * <p>
+ * <h4>Starting the StubFtpServer</h4>
+ * Here is how to start the <b>StubFtpServer</b> with the default configuration.
+ * <pre><code>
+ * StubFtpServer stubFtpServer = new StubFtpServer();
+ * stubFtpServer.start();
+ * </code></pre>
+ * <p>
+ * <h4>Retrieving Command Handlers</h4>
+ * You can retrieve the existing {@link CommandHandler} defined for an FTP server command
+ * by calling the {@link #getCommandHandler(String)} method, passing in the FTP server
+ * command name. For example:
+ * <pre><code>
+ * PwdCommandHandler pwdCommandHandler = (PwdCommandHandler) stubFtpServer.getCommandHandler("PWD");
+ * </code></pre>
+ * <p>
+ * <h4>Replacing Command Handlers</h4>
+ * You can replace the existing {@link CommandHandler} defined for an FTP server command
+ * by calling the {@link #setCommandHandler(String, CommandHandler)} method, passing
+ * in the FTP server command name and {@link CommandHandler} instance. For example:
+ * <pre><code>
+ * PwdCommandHandler pwdCommandHandler = new PwdCommandHandler();
+ * pwdCommandHandler.setDirectory("some/dir");
+ * stubFtpServer.setCommandHandler("PWD", pwdCommandHandler);
+ * </code></pre>
+ * You can also replace multiple command handlers at once by using the {@link #setCommandHandlers(Map)}
+ * method. That is especially useful when configuring the server through the <b>Spring Framework</b>.
+ * <h4>FTP Command Reply Text ResourceBundle</h4>
+ * <p>
+ * 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
+ * {@link #setReplyTextBaseName(String)} method.
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public final class StubFtpServer implements Runnable {
+
+ /** Default basename for reply text ResourceBundle */
+ public static final String REPLY_TEXT_BASENAME = "ReplyText";
+ private static final int DEFAULT_SERVER_CONTROL_PORT = 21;
+
+ private static Logger LOG = Logger.getLogger(StubFtpServer.class);
+
+ // Simple value object that holds the socket and thread for a single session
+ private static class SessionInfo {
+ private Socket socket;
+ private Thread thread;
+ }
+
+ private ServerSocketFactory serverSocketFactory = new DefaultServerSocketFactory();
+ private ServerSocket serverSocket = null;
+ ResourceBundle replyTextBundle; // non-private for testing only
+ private volatile boolean terminate = false;
+ private Map commandHandlers;
+ private Thread serverThread;
+ private int serverControlPort = DEFAULT_SERVER_CONTROL_PORT;
+ private Object startLock = new Object();
+
+ // Map of Session -> SessionInfo
+ private Map sessions = new HashMap();
+
+ /**
+ * Create a new instance. Initialize the default command handlers and
+ * reply text ResourceBundle.
+ */
+ public StubFtpServer() {
+ replyTextBundle = ResourceBundle.getBundle(REPLY_TEXT_BASENAME);
+
+ commandHandlers = new HashMap();
+
+ PwdCommandHandler pwdCommandHandler = new PwdCommandHandler();
+
+ // Initialize the default CommandHandler mappings
+ setCommandHandler(CommandNames.ABOR, new AborCommandHandler());
+ setCommandHandler(CommandNames.ACCT, new AcctCommandHandler());
+ setCommandHandler(CommandNames.ALLO, new AlloCommandHandler());
+ setCommandHandler(CommandNames.APPE, new AppeCommandHandler());
+ setCommandHandler(CommandNames.PWD, pwdCommandHandler); // same as XPWD
+ setCommandHandler(CommandNames.CONNECT, new ConnectCommandHandler());
+ setCommandHandler(CommandNames.CWD, new CwdCommandHandler());
+ setCommandHandler(CommandNames.CDUP, new CdupCommandHandler());
+ setCommandHandler(CommandNames.DELE, new DeleCommandHandler());
+ setCommandHandler(CommandNames.HELP, new HelpCommandHandler());
+ setCommandHandler(CommandNames.LIST, new ListCommandHandler());
+ setCommandHandler(CommandNames.MKD, new MkdCommandHandler());
+ setCommandHandler(CommandNames.MODE, new ModeCommandHandler());
+ setCommandHandler(CommandNames.NOOP, new NoopCommandHandler());
+ setCommandHandler(CommandNames.NLST, new NlstCommandHandler());
+ setCommandHandler(CommandNames.PASS, new PassCommandHandler());
+ setCommandHandler(CommandNames.PASV, new PasvCommandHandler());
+ setCommandHandler(CommandNames.PORT, new PortCommandHandler());
+ setCommandHandler(CommandNames.RETR, new RetrCommandHandler());
+ setCommandHandler(CommandNames.QUIT, new QuitCommandHandler());
+ setCommandHandler(CommandNames.REIN, new ReinCommandHandler());
+ setCommandHandler(CommandNames.REST, new RestCommandHandler());
+ setCommandHandler(CommandNames.RMD, new RmdCommandHandler());
+ setCommandHandler(CommandNames.RNFR, new RnfrCommandHandler());
+ setCommandHandler(CommandNames.RNTO, new RntoCommandHandler());
+ setCommandHandler(CommandNames.SITE, new SiteCommandHandler());
+ setCommandHandler(CommandNames.SMNT, new SmntCommandHandler());
+ setCommandHandler(CommandNames.STAT, new StatCommandHandler());
+ setCommandHandler(CommandNames.STOR, new StorCommandHandler());
+ setCommandHandler(CommandNames.STOU, new StouCommandHandler());
+ setCommandHandler(CommandNames.STRU, new StruCommandHandler());
+ setCommandHandler(CommandNames.SYST, new SystCommandHandler());
+ setCommandHandler(CommandNames.TYPE, new TypeCommandHandler());
+ setCommandHandler(CommandNames.USER, new UserCommandHandler());
+ setCommandHandler(CommandNames.XPWD, pwdCommandHandler); // same as PWD
+ }
+
+ /**
+ * Start a new Thread for this server instance
+ */
+ public void start() {
+ serverThread = new Thread(this);
+
+ // Wait until the thread is initialized
+ synchronized(startLock){
+ try {
+ // Start here in case server thread runs faster than main thread.
+ // See https://sourceforge.net/tracker/?func=detail&atid=1006533&aid=1925590&group_id=208647
+ serverThread.start();
+
+ startLock.wait();
+ }
+ catch (InterruptedException e) {
+ e.printStackTrace();
+ throw new MockFtpServerException(e);
+ }
+ }
+ }
+
+ /**
+ * The logic for the server thread
+ * @see java.lang.Runnable#run()
+ */
+ public void run() {
+ try {
+ LOG.info("Starting the server on port " + serverControlPort);
+ serverSocket = serverSocketFactory.createServerSocket(serverControlPort);
+
+ // Notify to allow the start() method to finish and return
+ synchronized(startLock) {
+ startLock.notify();
+ }
+
+ serverSocket.setSoTimeout(500);
+ while(!terminate) {
+ try {
+ Socket clientSocket = serverSocket.accept();
+ LOG.info("Connection accepted from host " + clientSocket.getInetAddress());
+
+ DefaultSession session = new DefaultSession(clientSocket, commandHandlers);
+ Thread sessionThread = new Thread(session);
+ sessionThread.start();
+
+ SessionInfo sessionInfo = new SessionInfo();
+ sessionInfo.socket = clientSocket;
+ sessionInfo.thread = sessionThread;
+ sessions.put(session, sessionInfo);
+ }
+ catch(SocketTimeoutException socketTimeoutException) {
+ LOG.trace("Socket accept() timeout");
+ }
+ }
+ }
+ catch (IOException e) {
+ LOG.error("Error", e);
+ }
+ finally {
+
+ LOG.debug("Cleaning up server...");
+
+ try {
+ serverSocket.close();
+
+ for (Iterator iter = sessions.keySet().iterator(); iter.hasNext();) {
+ Session session = (Session) iter.next();
+ SessionInfo sessionInfo = (SessionInfo) sessions.get(session);
+ session.close();
+ sessionInfo.thread.join(500L);
+ Socket sessionSocket = (Socket) sessionInfo.socket;
+ if (sessionSocket != null) {
+ sessionSocket.close();
+ }
+ }
+ }
+ catch (IOException e) {
+ e.printStackTrace();
+ throw new MockFtpServerException(e);
+ }
+ catch (InterruptedException e) {
+ e.printStackTrace();
+ throw new MockFtpServerException(e);
+ }
+ LOG.info("Server stopped.");
+ }
+ }
+
+ /**
+ * Stop this server instance and wait for it to terminate.
+ */
+ public void stop() {
+
+ LOG.trace("Stopping the server...");
+ terminate = true;
+
+ try {
+ serverThread.join();
+ }
+ catch (InterruptedException e) {
+ e.printStackTrace();
+ throw new MockFtpServerException(e);
+ }
+ }
+
+ /**
+ * Return the CommandHandler defined for the specified command name
+ * @param name - the command name
+ * @return the CommandHandler defined for name
+ */
+ public CommandHandler getCommandHandler(String name) {
+ return (CommandHandler) commandHandlers.get(Command.normalizeName(name));
+ }
+
+ /**
+ * Override the default CommandHandlers with those in the specified Map of
+ * commandName>>CommandHandler. This will only override the default CommandHandlers
+ * for the keys in <code>commandHandlerMapping</code>. All other default CommandHandler
+ * mappings remain unchanged.
+ *
+ * @param commandHandlers - the Map of commandName->CommandHandler; these override the defaults
+ *
+ * @throws AssertFailedException - if the commandHandlerMapping is null
+ */
+ public void setCommandHandlers(Map commandHandlerMapping) {
+ Assert.notNull(commandHandlerMapping, "commandHandlers");
+ for (Iterator iter = commandHandlerMapping.keySet().iterator(); iter.hasNext();) {
+ String commandName = (String) iter.next();
+ setCommandHandler(commandName, (CommandHandler) commandHandlerMapping.get(commandName));
+ }
+ }
+
+ /**
+ * Set the CommandHandler for the specified command name. If the CommandHandler implements
+ * the {@link ReplyTextBundleAware} interface and its <code>replyTextBundle</code> attribute
+ * is null, then set its <code>replyTextBundle</code> to the <code>replyTextBundle</code> of
+ * this StubFtpServer.
+ *
+ * @param commandName - the command name to which the CommandHandler will be associated
+ * @param commandHandler - the CommandHandler
+ *
+ * @throws AssertFailedException - if the commandName or commandHandler is null
+ */
+ public void setCommandHandler(String commandName, CommandHandler commandHandler) {
+ Assert.notNull(commandName, "commandName");
+ Assert.notNull(commandHandler, "commandHandler");
+ commandHandlers.put(Command.normalizeName(commandName), commandHandler);
+ ReplyTextBundleUtil.setReplyTextBundleIfAppropriate(commandHandler, replyTextBundle);
+ }
+
+ /**
+ * Set the reply text ResourceBundle to a new ResourceBundle with the specified base name,
+ * accessible on the CLASSPATH. See {@link ResourceBundle#getBundle(String)}.
+ * @param baseName - the base name of the resource bundle, a fully qualified class name
+ */
+ public void setReplyTextBaseName(String baseName) {
+ replyTextBundle = ResourceBundle.getBundle(baseName);
+ }
+
+ /**
+ * Set the port number to which the server control connection socket will bind. The default value is 21.
+ * @param serverControlPort - the port number for the server control connection ServerSocket
+ */
+ public void setServerControlPort(int serverControlPort) {
+ this.serverControlPort = serverControlPort;
+ }
+
+ //-------------------------------------------------------------------------
+ // Internal Helper Methods
+ //-------------------------------------------------------------------------
+
+ /**
+ * Return true if this server is fully shutdown -- i.e., there is no active (alive) threads and
+ * all sockets are closed. This method is intended for testing only.
+ * @return true if this server is fully shutdown
+ */
+ boolean isShutdown() {
+ boolean shutdown = !serverThread.isAlive() && serverSocket.isClosed();
+
+ for (Iterator iter = sessions.keySet().iterator(); iter.hasNext();) {
+ SessionInfo sessionInfo = (SessionInfo) iter.next();
+ shutdown = shutdown && sessionInfo.socket.isClosed() && !sessionInfo.thread.isAlive();
+ }
+ return shutdown;
+ }
+
+ /**
+ * Return true if this server has started -- i.e., there is an active (alive) server threads
+ * and non-null server socket. This method is intended for testing only.
+ * @return true if this server has started
+ */
+ boolean isStarted() {
+ return serverThread != null && serverThread.isAlive() && serverSocket != null;
+ }
+
+} \ No newline at end of file
diff --git a/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/AborCommandHandler.java b/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/AborCommandHandler.java
new file mode 100644
index 0000000..c1b2bce
--- /dev/null
+++ b/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/AborCommandHandler.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.stub.command;
+
+import org.mockftpserver.core.command.Command;
+import org.mockftpserver.core.command.CommandHandler;
+import org.mockftpserver.core.command.InvocationRecord;
+import org.mockftpserver.core.command.ReplyCodes;
+import org.mockftpserver.core.session.Session;
+
+/**
+ * CommandHandler for the ABOR command. Return a reply code of 226.
+ * <p>
+ * Each invocation record stored by this CommandHandler contains no data elements.
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public final class AborCommandHandler extends AbstractStubCommandHandler implements CommandHandler {
+
+ /**
+ * Constructor. Initialize the replyCode.
+ */
+ public AborCommandHandler() {
+ setReplyCode(ReplyCodes.ABOR_OK);
+ }
+
+ /**
+ * @see org.mockftpserver.core.command.CommandHandler#handleCommand(Command, Session, InvocationRecord)
+ */
+ public void handleCommand(Command command, Session session, InvocationRecord invocationRecord) {
+ sendReply(session);
+ }
+
+}
diff --git a/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/AbstractStubCommandHandler.java b/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/AbstractStubCommandHandler.java
new file mode 100644
index 0000000..be9919c
--- /dev/null
+++ b/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/AbstractStubCommandHandler.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.stub.command;
+
+import org.mockftpserver.core.command.AbstractCommandHandler;
+import org.mockftpserver.core.session.Session;
+import org.mockftpserver.core.util.AssertFailedException;
+import org.mockftpserver.stub.StubFtpServer;
+
+/**
+ * The abstract superclass for CommandHandler classes for the {@link StubFtpServer}.
+ * <p>
+ * Subclasses can optionally override the reply code and/or text for the reply by calling
+ * {@link #setReplyCode(int)}, {@link #setReplyMessageKey(String)} and {@link #setReplyText(String)}.
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public abstract class AbstractStubCommandHandler extends AbstractCommandHandler {
+
+ // Defaults to zero; must be set to non-zero
+ protected int replyCode = 0;
+
+ // Defaults to null; if set to non-null, this value will override the default reply text associated with
+ // the replyCode.
+ protected String replyText = null;
+
+ // The message key for the reply text. Defaults to null. If null, use the default message associated
+ // with the reply code
+ protected String replyMessageKey = null;
+
+ /**
+ * Set the reply code.
+ *
+ * @param replyCode - the replyCode
+ *
+ * @throws AssertFailedException - if the replyCode is not valid
+ */
+ public void setReplyCode(int replyCode) {
+ assertValidReplyCode(replyCode);
+ this.replyCode = replyCode;
+ }
+
+ /**
+ * Set the reply text. If null, then use the (default) message key for the replyCode.
+ *
+ * @param replyText - the replyText
+ */
+ public void setReplyText(String replyText) {
+ this.replyText = replyText;
+ }
+
+ /**
+ * Set the message key for the reply text. If null, then use the default message key.
+ *
+ * @param replyMessageKey - the replyMessageKey to set
+ */
+ public void setReplyMessageKey(String replyMessageKey) {
+ this.replyMessageKey = replyMessageKey;
+ }
+
+ // -------------------------------------------------------------------------
+ // Utility methods for subclasses
+ // -------------------------------------------------------------------------
+
+ /**
+ * Send the reply using the replyCode and message key/text configured for this command handler.
+ * @param session - the Session
+ *
+ * @throws AssertFailedException if the replyCode is not valid
+ */
+ protected void sendReply(Session session) {
+ sendReply(session, null);
+ }
+
+ /**
+ * Send the reply using the replyCode and message key/text configured for this command handler.
+ * @param session - the Session
+ * @param messageParameter - message parameter; may be null
+ *
+ * @throws AssertFailedException if the replyCode is not valid
+ */
+ protected void sendReply(Session session, Object messageParameter) {
+ Object[] parameters = (messageParameter == null) ? null : new Object[] { messageParameter };
+ sendReply(session, replyCode, replyMessageKey, replyText, parameters);
+ }
+
+}
diff --git a/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/AbstractStubDataCommandHandler.java b/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/AbstractStubDataCommandHandler.java
new file mode 100644
index 0000000..716fa04
--- /dev/null
+++ b/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/AbstractStubDataCommandHandler.java
@@ -0,0 +1,229 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.stub.command;
+
+import org.mockftpserver.core.command.AbstractCommandHandler;
+import org.mockftpserver.core.command.Command;
+import org.mockftpserver.core.command.CommandHandler;
+import org.mockftpserver.core.command.InvocationRecord;
+import org.mockftpserver.core.command.ReplyCodes;
+import org.mockftpserver.core.session.Session;
+import org.mockftpserver.core.util.AssertFailedException;
+
+/**
+ * Abstract superclass for CommandHandlers that read from or write to the data connection.
+ * <p>
+ * Return two replies on the control connection: by default a reply code of 150 before the
+ * data transfer across the data connection and another reply of 226 after the data transfer
+ * is complete.
+ * <p>
+ * This class implements the <i>Template Method</i> pattern. Subclasses must implement the abstract
+ * {@link #processData()} method to perform read or writes across the data connection.
+ * <p>
+ * Subclasses can optionally override the {@link #beforeProcessData(Command, Session, InvocationRecord)}
+ * method for logic before the data transfer or the {@link #afterProcessData(Command, Session, InvocationRecord)}
+ * method for logic after the data transfer.
+ * <p>
+ * Subclasses can optionally override the reply code and/or text for the initial reply (before
+ * the data transfer across the data connection) by calling {@link #setPreliminaryReplyCode(int)},
+ * {@link #setPreliminaryReplyMessageKey(String)} and/or {@link #setPreliminaryReplyText(String)}
+ * methods.
+ * <p>
+ * Subclasses can optionally override the reply code and/or text for the final reply (after the
+ * the data transfer is complete) by calling {@link #setFinalReplyCode(int)},
+ * {@link #setFinalReplyMessageKey(String)} and/or {@link #setFinalReplyText(String)} methods.
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public abstract class AbstractStubDataCommandHandler extends AbstractCommandHandler implements CommandHandler {
+
+ // The completion reply code sent before the data transfer
+ protected int preliminaryReplyCode = 0;
+
+ // The text for the preliminary reply. If null, use the default message associated with the reply code.
+ // If not null, this value overrides the preliminaryReplyMessageKey - i.e., this text is used instead of
+ // a localized message.
+ protected String preliminaryReplyText = null;
+
+ // The message key for the preliminary reply text. If null, use the default message associated with
+ // the reply code.
+ protected String preliminaryReplyMessageKey = null;
+
+ // The completion reply code sent after data transfer
+ protected int finalReplyCode = 0;
+
+ // The text for the completion reply. If null, use the default message associated with the reply code.
+ // If not null, this value overrides the finalReplyMessageKey - i.e., this text is used instead of
+ // a localized message.
+ protected String finalReplyText = null;
+
+ // The message key for the completion reply text. If null, use the default message associated with the reply code
+ protected String finalReplyMessageKey = null;
+
+ /**
+ * Constructor. Initialize the preliminary and final reply code.
+ */
+ protected AbstractStubDataCommandHandler() {
+ setPreliminaryReplyCode(ReplyCodes.SEND_DATA_INITIAL_OK);
+ setFinalReplyCode(ReplyCodes.SEND_DATA_FINAL_OK);
+ }
+
+ /**
+ * Handle the command. Perform the following steps:
+ * <ol>
+ * <li>Invoke the {@link #beforeProcessData()} method</li>
+ * <li>Open the data connection</li>
+ * <li>Send an preliminary reply, default reply code 150</li>
+ * <li>Invoke the {@link #processData()} method</li>
+ * <li>Close the data connection</li>
+ * <li>Send the final reply, default reply code 226</li>
+ * <li>Invoke the {@link #afterProcessData()} method</li>
+ * </ol>
+ *
+ * @see org.mockftpserver.core.command.CommandHandler#handleCommand(Command, Session, InvocationRecord)
+ */
+ public final void handleCommand(Command command, Session session, InvocationRecord invocationRecord) throws Exception {
+
+ beforeProcessData(command, session, invocationRecord);
+
+ sendPreliminaryReply(session);
+ session.openDataConnection();
+ processData(command, session, invocationRecord);
+ session.closeDataConnection();
+ sendFinalReply(session);
+
+ afterProcessData(command, session, invocationRecord);
+ }
+
+ /**
+ * Send the final reply. The default implementation sends a reply code of 226 with the
+ * corresponding associated reply text.
+ * @param session - the Session
+ */
+ protected void sendFinalReply(Session session) {
+ sendReply(session, finalReplyCode, finalReplyMessageKey, finalReplyText, null);
+ }
+
+ /**
+ * Perform any necessary logic before transferring data across the data connection.
+ * Do nothing by default. Subclasses should override to validate command parameters and
+ * store information in the InvocationRecord.
+ *
+ * @param command - the Command to be handled
+ * @param session - the session on which the Command was submitted
+ * @param invocationRecord - the InvocationRecord; CommandHandlers are expected to add
+ * handler-specific data to the InvocationRecord, as appropriate
+ *
+ * @throws Exception
+ */
+ protected void beforeProcessData(Command command, Session session, InvocationRecord invocationRecord) throws Exception {
+ // Do nothing by default
+ }
+
+ /**
+ * Abstract method placeholder for subclass transfer of data across the data connection.
+ * Subclasses must override. The data connection is opened before this method and is
+ * closed after this method completes.
+ *
+ * @param command - the Command to be handled
+ * @param session - the session on which the Command was submitted
+ * @param invocationRecord - the InvocationRecord; CommandHandlers are expected to add
+ * handler-specific data to the InvocationRecord, as appropriate
+ *
+ * @throws Exception
+ */
+ protected abstract void processData(Command command, Session session, InvocationRecord invocationRecord) throws Exception;
+
+ /**
+ * Perform any necessary logic after transferring data across the data connection.
+ * Do nothing by default.
+ *
+ * @param command - the Command to be handled
+ * @param session - the session on which the Command was submitted
+ * @param invocationRecord - the InvocationRecord; CommandHandlers are expected to add
+ * handler-specific data to the InvocationRecord, as appropriate
+ * @throws Exception
+ */
+ protected void afterProcessData(Command command, Session session, InvocationRecord invocationRecord) throws Exception {
+ // Do nothing by default
+ }
+
+ /**
+ * Send the preliminary reply for this command on the control connection.
+ *
+ * @param session - the Session
+ */
+ private void sendPreliminaryReply(Session session) {
+ sendReply(session, preliminaryReplyCode, preliminaryReplyMessageKey, preliminaryReplyText, null);
+ }
+
+ /**
+ * Set the completion reply code sent after data transfer
+ * @param finalReplyCode - the final reply code
+ *
+ * @throws AssertFailedException - if the finalReplyCode is invalid
+ */
+ public void setFinalReplyCode(int finalReplyCode) {
+ assertValidReplyCode(finalReplyCode);
+ this.finalReplyCode = finalReplyCode;
+ }
+
+ /**
+ * Set the message key for the completion reply text sent after data transfer
+ * @param finalReplyMessageKey - the final reply message key
+ */
+ public void setFinalReplyMessageKey(String finalReplyMessageKey) {
+ this.finalReplyMessageKey = finalReplyMessageKey;
+ }
+
+ /**
+ * Set the text of the completion reply sent after data transfer
+ * @param finalReplyText - the final reply text
+ */
+ public void setFinalReplyText(String finalReplyText) {
+ this.finalReplyText = finalReplyText;
+ }
+
+ /**
+ * Set the completion reply code sent before data transfer
+ * @param preliminaryReplyCode - the preliminary reply code to set
+ *
+ * @throws AssertFailedException - if the preliminaryReplyCode is invalid
+ */
+ public void setPreliminaryReplyCode(int preliminaryReplyCode) {
+ assertValidReplyCode(preliminaryReplyCode);
+ this.preliminaryReplyCode = preliminaryReplyCode;
+ }
+
+ /**
+ * Set the message key for the completion reply text sent before data transfer
+ * @param preliminaryReplyMessageKey - the preliminary reply message key
+ */
+ public void setPreliminaryReplyMessageKey(String preliminaryReplyMessageKey) {
+ this.preliminaryReplyMessageKey = preliminaryReplyMessageKey;
+ }
+
+ /**
+ * Set the text of the completion reply sent before data transfer
+ * @param preliminaryReplyText - the preliminary reply text
+ */
+ public void setPreliminaryReplyText(String preliminaryReplyText) {
+ this.preliminaryReplyText = preliminaryReplyText;
+ }
+
+}
diff --git a/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/AcctCommandHandler.java b/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/AcctCommandHandler.java
new file mode 100644
index 0000000..0d828f2
--- /dev/null
+++ b/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/AcctCommandHandler.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.stub.command;
+
+import org.mockftpserver.core.command.Command;
+import org.mockftpserver.core.command.CommandHandler;
+import org.mockftpserver.core.command.InvocationRecord;
+import org.mockftpserver.core.command.ReplyCodes;
+import org.mockftpserver.core.session.Session;
+
+/**
+ * CommandHandler for the ACCT command. Send back a reply code of 230.
+ * <p>
+ * Each invocation record stored by this CommandHandler includes the following data element key/values:
+ * <ul>
+ * <li>"acount" - the account submitted on the invocation (the first command parameter)
+ * </ul>
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public final class AcctCommandHandler extends AbstractStubCommandHandler implements CommandHandler {
+
+ public static final String ACCOUNT_KEY = "account";
+
+ /**
+ * Constructor. Initialize the replyCode.
+ */
+ public AcctCommandHandler() {
+ setReplyCode(ReplyCodes.ACCT_OK);
+ }
+
+ /**
+ * @see org.mockftpserver.core.command.CommandHandler#handleCommand(Command, Session, InvocationRecord)
+ */
+ public void handleCommand(Command command, Session session, InvocationRecord invocationRecord) {
+ invocationRecord.set(ACCOUNT_KEY, command.getRequiredString(0));
+ sendReply(session);
+ }
+
+}
diff --git a/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/AlloCommandHandler.java b/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/AlloCommandHandler.java
new file mode 100644
index 0000000..cee4b46
--- /dev/null
+++ b/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/AlloCommandHandler.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.stub.command;
+
+import java.util.StringTokenizer;
+
+import org.mockftpserver.core.command.Command;
+import org.mockftpserver.core.command.CommandHandler;
+import org.mockftpserver.core.command.InvocationRecord;
+import org.mockftpserver.core.command.ReplyCodes;
+import org.mockftpserver.core.session.Session;
+import org.mockftpserver.core.util.Assert;
+
+/**
+ * CommandHandler for the ALLO (Allocate) command. Send back a reply code of 200.
+ * <p>
+ * Each invocation record stored by this CommandHandler includes the following data element key/values:
+ * <ul>
+ * <li>{@link #NUMBER_OF_BYTES_KEY} ("numberOfBytes") - the number of bytes submitted
+ * on the invocation (the first command parameter)
+ * <li>{@link #RECORD_SIZE_KEY} ("recordSize") - the record size optionally submitted
+ * on the invocation (the second command parameter)
+ * </ul>
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public final class AlloCommandHandler extends AbstractStubCommandHandler implements CommandHandler {
+
+ public static final String NUMBER_OF_BYTES_KEY = "numberOfBytes";
+ public static final String RECORD_SIZE_KEY = "recordSize";
+ private static final String RECORD_SIZE_DELIMITER = " R ";
+
+ /**
+ * Constructor. Initialize the replyCode.
+ */
+ public AlloCommandHandler() {
+ setReplyCode(ReplyCodes.ALLO_OK);
+ }
+
+ /**
+ * @see org.mockftpserver.core.command.CommandHandler#handleCommand(Command, Session, InvocationRecord)
+ */
+ public void handleCommand(Command command, Session session, InvocationRecord invocationRecord) {
+ String parametersString = command.getRequiredString(0);
+
+ if (parametersString.indexOf(RECORD_SIZE_DELIMITER) == -1) {
+ invocationRecord.set(NUMBER_OF_BYTES_KEY, Integer.valueOf(parametersString));
+ }
+ else {
+ // If the recordSize delimiter (" R ") is specified, then it must be followed by the recordSize.
+ StringTokenizer tokenizer = new StringTokenizer(parametersString, RECORD_SIZE_DELIMITER);
+ invocationRecord.set(NUMBER_OF_BYTES_KEY, Integer.valueOf(tokenizer.nextToken()));
+ Assert.isTrue(tokenizer.hasMoreTokens(), "Missing record size: [" + parametersString + "]");
+ invocationRecord.set(RECORD_SIZE_KEY, Integer.valueOf(tokenizer.nextToken()));
+ }
+
+ sendReply(session);
+ }
+
+}
diff --git a/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/AppeCommandHandler.java b/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/AppeCommandHandler.java
new file mode 100644
index 0000000..48ff0cd
--- /dev/null
+++ b/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/AppeCommandHandler.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.stub.command;
+
+import org.apache.log4j.Logger;
+import org.mockftpserver.core.command.Command;
+import org.mockftpserver.core.command.CommandHandler;
+import org.mockftpserver.core.command.InvocationRecord;
+import org.mockftpserver.core.session.Session;
+
+/**
+ * CommandHandler for the APPE (Append) command. Send back two replies on the control connection: a
+ * reply code of 150 and another of 226.
+ * <p>
+ * Each invocation record stored by this CommandHandler includes the following data element key/values:
+ * <ul>
+ * <li>{@link #PATHNAME_KEY} ("pathname") - the pathname of the directory submitted on the invocation (the first command parameter)
+ * <li>{@link #FILE_CONTENTS_KEY} ("fileContents") - the file contents (<code>byte[]</code>) sent on the data connection
+ * </ul>
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public final class AppeCommandHandler extends AbstractStubDataCommandHandler implements CommandHandler {
+
+ public static final String PATHNAME_KEY = "pathname";
+ public static final String FILE_CONTENTS_KEY = "filecontents";
+
+ private static final Logger LOG = Logger.getLogger(AppeCommandHandler.class);
+
+ /**
+ * @see org.mockftpserver.stub.command.AbstractStubDataCommandHandler#beforeProcessData(org.mockftpserver.core.command.Command, org.mockftpserver.core.session.Session, org.mockftpserver.core.command.InvocationRecord)
+ */
+ protected void beforeProcessData(Command command, Session session, InvocationRecord invocationRecord) throws Exception {
+ String filename = command.getRequiredString(0);
+ invocationRecord.set(PATHNAME_KEY, filename);
+ }
+
+ /**
+ * @see org.mockftpserver.stub.command.AbstractStubDataCommandHandler#processData(org.mockftpserver.core.command.Command, org.mockftpserver.core.session.Session, org.mockftpserver.core.command.InvocationRecord)
+ */
+ protected void processData(Command command, Session session, InvocationRecord invocationRecord) {
+ byte[] data = session.readData();
+ LOG.info("Received " + data.length + " bytes");
+ LOG.trace("Received data [" + new String(data) + "]");
+ invocationRecord.set(FILE_CONTENTS_KEY, data);
+ }
+
+}
diff --git a/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/CdupCommandHandler.java b/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/CdupCommandHandler.java
new file mode 100644
index 0000000..928137b
--- /dev/null
+++ b/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/CdupCommandHandler.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.stub.command;
+
+import org.mockftpserver.core.command.Command;
+import org.mockftpserver.core.command.CommandHandler;
+import org.mockftpserver.core.command.InvocationRecord;
+import org.mockftpserver.core.command.ReplyCodes;
+import org.mockftpserver.core.session.Session;
+
+/**
+ * CommandHandler for the CDUP (Change To Parent Directory) command. Send back a reply code of 250.
+ * <p>
+ * Each invocation record stored by this CommandHandler contains no data elements.
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public final class CdupCommandHandler extends AbstractStubCommandHandler implements CommandHandler {
+
+ /**
+ * Constructor. Initialize the replyCode.
+ */
+ public CdupCommandHandler() {
+ setReplyCode(ReplyCodes.CDUP_OK);
+ }
+
+ /**
+ * @see org.mockftpserver.core.command.CommandHandler#handleCommand(Command, Session, InvocationRecord)
+ */
+ public void handleCommand(Command command, Session session, InvocationRecord invocationRecord) {
+ sendReply(session);
+ }
+
+}
diff --git a/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/ConnectCommandHandler.java b/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/ConnectCommandHandler.java
new file mode 100644
index 0000000..8817f80
--- /dev/null
+++ b/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/ConnectCommandHandler.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.stub.command;
+
+import org.mockftpserver.core.command.Command;
+import org.mockftpserver.core.command.CommandHandler;
+import org.mockftpserver.core.command.InvocationRecord;
+import org.mockftpserver.core.command.ReplyCodes;
+import org.mockftpserver.core.session.Session;
+
+/**
+ * CommandHandler that encapsulates the sending of the reply for the initial
+ * connection from the FTP client to the server. Send back a reply code of 220,
+ * indicating a successful connection.
+ * <p>
+ * Note that this is a "special" CommandHandler, in that it handles the initial
+ * connection from the client, rather than an explicit FTP command.
+ * <p>
+ * Each invocation record stored by this CommandHandler contains no data elements.
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public final class ConnectCommandHandler extends AbstractStubCommandHandler implements CommandHandler {
+
+ /**
+ * Constructor. Initiate the replyCode.
+ */
+ public ConnectCommandHandler() {
+ setReplyCode(ReplyCodes.CONNECT_OK);
+ }
+
+ /**
+ * @see org.mockftpserver.core.command.CommandHandler#handleCommand(Command, Session, InvocationRecord)
+ */
+ public void handleCommand(Command command, Session session, InvocationRecord invocationRecord) {
+ sendReply(session);
+ }
+
+}
diff --git a/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/CwdCommandHandler.java b/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/CwdCommandHandler.java
new file mode 100644
index 0000000..61df65e
--- /dev/null
+++ b/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/CwdCommandHandler.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.stub.command;
+
+import org.mockftpserver.core.command.Command;
+import org.mockftpserver.core.command.CommandHandler;
+import org.mockftpserver.core.command.InvocationRecord;
+import org.mockftpserver.core.command.ReplyCodes;
+import org.mockftpserver.core.session.Session;
+
+/**
+ * CommandHandler for the CWD (Change Working Directory) command. Send back a reply code of 250.
+ * <p>
+ * Each invocation record stored by this CommandHandler includes the following data element key/values:
+ * <ul>
+ * <li>{@link #PATHNAME_KEY} ("pathname") - the pathname of the directory submitted on the invocation (the first command parameter)
+ * </ul>
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public final class CwdCommandHandler extends AbstractStubCommandHandler implements CommandHandler {
+
+ public static final String PATHNAME_KEY = "pathname";
+
+ /**
+ * Constructor. Initiate the replyCode.
+ */
+ public CwdCommandHandler() {
+ setReplyCode(ReplyCodes.CWD_OK);
+ }
+
+ /**
+ * @see org.mockftpserver.core.command.CommandHandler#handleCommand(Command, Session, InvocationRecord)
+ */
+ public void handleCommand(Command command, Session session, InvocationRecord invocationRecord) {
+ invocationRecord.set(PATHNAME_KEY, command.getRequiredString(0));
+ sendReply(session);
+ }
+
+}
diff --git a/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/DeleCommandHandler.java b/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/DeleCommandHandler.java
new file mode 100644
index 0000000..523c3ff
--- /dev/null
+++ b/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/DeleCommandHandler.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.stub.command;
+
+import org.mockftpserver.core.command.Command;
+import org.mockftpserver.core.command.CommandHandler;
+import org.mockftpserver.core.command.InvocationRecord;
+import org.mockftpserver.core.command.ReplyCodes;
+import org.mockftpserver.core.session.Session;
+
+/**
+ * CommandHandler for the DELE (Delete) command. Send back a reply code of 250.
+ * <p>
+ * Each invocation record stored by this CommandHandler includes the following data element key/values:
+ * <ul>
+ * <li>{@link #PATHNAME_KEY} ("pathname") - the pathname of the file name submitted on the invocation (the first command parameter)
+ * </ul>
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public final class DeleCommandHandler extends AbstractStubCommandHandler implements CommandHandler {
+
+ public static final String PATHNAME_KEY = "pathname";
+
+ /**
+ * Constructor. Initialize the replyCode.
+ */
+ public DeleCommandHandler() {
+ setReplyCode(ReplyCodes.DELE_OK);
+ }
+
+ /**
+ * @see org.mockftpserver.core.command.CommandHandler#handleCommand(Command, Session, InvocationRecord)
+ */
+ public void handleCommand(Command command, Session session, InvocationRecord invocationRecord) {
+ invocationRecord.set(PATHNAME_KEY, command.getRequiredString(0));
+ sendReply(session);
+ }
+
+}
diff --git a/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/FileRetrCommandHandler.java b/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/FileRetrCommandHandler.java
new file mode 100644
index 0000000..cc83b82
--- /dev/null
+++ b/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/FileRetrCommandHandler.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.stub.command;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+
+import org.apache.log4j.Logger;
+import org.mockftpserver.core.MockFtpServerException;
+import org.mockftpserver.core.command.Command;
+import org.mockftpserver.core.command.CommandHandler;
+import org.mockftpserver.core.command.InvocationRecord;
+import org.mockftpserver.core.session.Session;
+import org.mockftpserver.core.util.Assert;
+import org.mockftpserver.core.util.AssertFailedException;
+
+/**
+ * CommandHandler for the RETR command. Returns the contents of the specified file on the
+ * data connection, along with two replies on the control connection: a reply code of 150 and
+ * another of 226.
+ * <p>
+ * The <code>file</code> property specifies the pathname for the file whose contents should
+ * be returned from this command. The file path is relative to the CLASSPATH (using the
+ * ClassLoader for this class).
+ * <p>
+ * An exception is thrown if the <code>file</code> property has not been set or if the specified
+ * file does not exist or cannot be read.
+ * <p>
+ * Each invocation record stored by this CommandHandler includes the following data element key/values:
+ * <ul>
+ * <li>{@link #PATHNAME_KEY} ("pathname") - the pathname of the file submitted on the invocation (the first command parameter)
+ * </ul>
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public final class FileRetrCommandHandler extends AbstractStubDataCommandHandler implements CommandHandler {
+
+ public static final String PATHNAME_KEY = "pathname";
+ static final int BUFFER_SIZE = 512; // package-private for testing
+ private static final Logger LOG = Logger.getLogger(FileRetrCommandHandler.class);
+
+ private String file;
+
+ /**
+ * Create new uninitialized instance
+ */
+ public FileRetrCommandHandler() {
+ }
+
+ /**
+ * Create new instance using the specified file pathname
+ * @param file - the path to the file
+ * @throws AssertFailedException - if the file is null
+ */
+ public FileRetrCommandHandler(String file) {
+ setFile(file);
+ }
+
+ /**
+ * @see org.mockftpserver.stub.command.AbstractStubDataCommandHandler#beforeProcessData(org.mockftpserver.core.command.Command, org.mockftpserver.core.session.Session, org.mockftpserver.core.command.InvocationRecord)
+ */
+ protected void beforeProcessData(Command command, Session session, InvocationRecord invocationRecord) throws Exception {
+ Assert.notNull(file, "file");
+ invocationRecord.set(PATHNAME_KEY, command.getRequiredString(0));
+ }
+
+ /**
+ * @see org.mockftpserver.stub.command.AbstractStubDataCommandHandler#processData(org.mockftpserver.core.command.Command, org.mockftpserver.core.session.Session, org.mockftpserver.core.command.InvocationRecord)
+ */
+ protected void processData(Command command, Session session, InvocationRecord invocationRecord) {
+ InputStream inputStream = getClass().getClassLoader().getResourceAsStream(file);
+ Assert.notNull(inputStream, "InputStream for [" + file + "]");
+ byte[] buffer = new byte[BUFFER_SIZE];
+ try {
+ int numBytes = 0;
+ while ((numBytes = inputStream.read(buffer)) != -1) {
+ LOG.trace("Sending " + numBytes + " bytes...");
+ session.sendData(buffer, numBytes);
+ }
+ }
+ catch(IOException e) {
+ throw new MockFtpServerException(e);
+ }
+ }
+
+ /**
+ * Set the path of the file whose contents should be returned when this command is
+ * invoked. The path is relative to the CLASSPATH.
+ * @param file - the path to the file
+ * @throws AssertFailedException - if the file is null
+ */
+ public void setFile(String file) {
+ Assert.notNull(file, "file");
+ this.file = file;
+ }
+
+}
diff --git a/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/HelpCommandHandler.java b/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/HelpCommandHandler.java
new file mode 100644
index 0000000..4a072e2
--- /dev/null
+++ b/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/HelpCommandHandler.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.stub.command;
+
+import org.mockftpserver.core.command.Command;
+import org.mockftpserver.core.command.CommandHandler;
+import org.mockftpserver.core.command.InvocationRecord;
+import org.mockftpserver.core.command.ReplyCodes;
+import org.mockftpserver.core.session.Session;
+
+/**
+ * CommandHandler for the HELP command. By default, return an empty help message,
+ * along with a reply code of 214. You can customize the returned help message by
+ * setting the <code>helpMessage</code> property.
+ * <p>
+ * Each invocation record stored by this CommandHandler includes the following data element key/values:
+ * <ul>
+ * <li>{@link #COMMAND_NAME_KEY} ("commandName") - the command name optionally submitted on
+ * the invocation (the first command parameter). May be null.
+ * </ul>
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public final class HelpCommandHandler extends AbstractStubCommandHandler implements CommandHandler {
+
+ public static final String COMMAND_NAME_KEY = "commandName";
+
+ private String helpMessage = "";
+
+ /**
+ * Constructor. Initialize the replyCode.
+ */
+ public HelpCommandHandler() {
+ setReplyCode(ReplyCodes.HELP_OK);
+ }
+
+ /**
+ * @see org.mockftpserver.core.command.CommandHandler#handleCommand(Command, Session, InvocationRecord)
+ */
+ public void handleCommand(Command command, Session session, InvocationRecord invocationRecord) {
+ invocationRecord.set(COMMAND_NAME_KEY, command.getOptionalString(0));
+ sendReply(session, helpMessage);
+ }
+
+ /**
+ * Set the help message String to be returned by this command
+ * @param directory - the directory
+ */
+ public void setHelpMessage(String response) {
+ this.helpMessage = response;
+ }
+
+}
diff --git a/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/ListCommandHandler.java b/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/ListCommandHandler.java
new file mode 100644
index 0000000..29335c8
--- /dev/null
+++ b/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/ListCommandHandler.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.stub.command;
+
+import org.mockftpserver.core.command.Command;
+import org.mockftpserver.core.command.CommandHandler;
+import org.mockftpserver.core.command.InvocationRecord;
+import org.mockftpserver.core.session.Session;
+
+/**
+ * CommandHandler for the LIST command. Return the configured directory listing on the data
+ * connection, along with two replies on the control connection: a reply code of 150 and
+ * another of 226. By default, return an empty directory listing. You can customize the
+ * returned directory listing by setting the <code>directoryListing</code> property.
+ * <p>
+ * The interpretation of the value returned from this command is dependent upon the value returned
+ * by the SYST command. The format of the directory listing should match the format associated with
+ * the system named by the SYST command. For example, if the SYST command returns "WINDOWS", then
+ * the directory listing value from this command should match the Windows-specific format.
+ * <p>
+ * Each invocation record stored by this CommandHandler includes the following data element key/values:
+ * <ul>
+ * <li>{@link #PATHNAME_KEY} ("pathname") - the pathname of the directory (or file) submitted on the
+ * invocation (the first command parameter); this parameter is optional, so the value may be null.
+ * </ul>
+ *
+ * @see SystCommandHandler
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public final class ListCommandHandler extends AbstractStubDataCommandHandler implements CommandHandler {
+
+ public static final String PATHNAME_KEY = "pathname";
+
+ private String directoryListing = "";
+
+ /**
+ * @see org.mockftpserver.stub.command.AbstractStubDataCommandHandler#beforeProcessData(org.mockftpserver.core.command.Command, org.mockftpserver.core.session.Session, org.mockftpserver.core.command.InvocationRecord)
+ */
+ protected void beforeProcessData(Command command, Session session, InvocationRecord invocationRecord) throws Exception {
+ invocationRecord.set(PATHNAME_KEY, command.getOptionalString(0));
+ }
+
+ /**
+ * @see org.mockftpserver.stub.command.AbstractStubDataCommandHandler#processData(org.mockftpserver.core.command.Command, org.mockftpserver.core.session.Session, org.mockftpserver.core.command.InvocationRecord)
+ */
+ protected void processData(Command command, Session session, InvocationRecord invocationRecord) {
+ session.sendData(directoryListing.getBytes(), directoryListing.length());
+ }
+
+ /**
+ * Set the contents of the directoryListing to send back on the data connection for this command.
+ * The passed-in value is trimmed automatically.
+ * @param directoryListing - the directoryListing to set
+ */
+ public void setDirectoryListing(String directoryListing) {
+ this.directoryListing = directoryListing.trim();
+ }
+
+}
diff --git a/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/MkdCommandHandler.java b/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/MkdCommandHandler.java
new file mode 100644
index 0000000..e0912f4
--- /dev/null
+++ b/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/MkdCommandHandler.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.stub.command;
+
+import org.mockftpserver.core.command.Command;
+import org.mockftpserver.core.command.CommandHandler;
+import org.mockftpserver.core.command.InvocationRecord;
+import org.mockftpserver.core.command.ReplyCodes;
+import org.mockftpserver.core.session.Session;
+
+/**
+ * CommandHandler for the MKD (Make Directory) command. Send back a reply code of 257.
+ * <p>
+ * Each invocation record stored by this CommandHandler includes the following data element key/values:
+ * <ul>
+ * <li>{@link #PATHNAME_KEY} ("pathname") - the pathname of the directory submitted on the invocation (the first command parameter)
+ * </ul>
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public final class MkdCommandHandler extends AbstractStubCommandHandler implements CommandHandler {
+
+ public static final String PATHNAME_KEY = "pathname";
+
+ /**
+ * Constructor. Initialize the replyCode.
+ */
+ public MkdCommandHandler() {
+ setReplyCode(ReplyCodes.MKD_OK);
+ }
+
+ /**
+ * @see org.mockftpserver.core.command.CommandHandler#handleCommand(Command, Session, InvocationRecord)
+ */
+ public void handleCommand(Command command, Session session, InvocationRecord invocationRecord) {
+ String pathname = command.getRequiredString(0);
+ invocationRecord.set(PATHNAME_KEY, pathname);
+ sendReply(session, pathname);
+ }
+
+}
diff --git a/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/ModeCommandHandler.java b/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/ModeCommandHandler.java
new file mode 100644
index 0000000..3ff6923
--- /dev/null
+++ b/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/ModeCommandHandler.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.stub.command;
+
+import org.mockftpserver.core.command.Command;
+import org.mockftpserver.core.command.CommandHandler;
+import org.mockftpserver.core.command.InvocationRecord;
+import org.mockftpserver.core.command.ReplyCodes;
+import org.mockftpserver.core.session.Session;
+
+/**
+ * CommandHandler for the MODE command. Send back a reply code of 200.
+ * <p>
+ * Each invocation record stored by this CommandHandler includes the following data element key/values:
+ * <ul>
+ * <li>{@link #MODE_KEY} ("mode") - the code for the transmission mode submitted on the invocation (the first command parameter)
+ * </ul>
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public final class ModeCommandHandler extends AbstractStubCommandHandler implements CommandHandler {
+
+ public static final String MODE_KEY = "mode";
+
+ /**
+ * Constructor. Initialize the replyCode.
+ */
+ public ModeCommandHandler() {
+ setReplyCode(ReplyCodes.MODE_OK);
+ }
+
+ /**
+ * @see org.mockftpserver.core.command.CommandHandler#handleCommand(Command, Session, InvocationRecord)
+ */
+ public void handleCommand(Command command, Session session, InvocationRecord invocationRecord) {
+ invocationRecord.set(MODE_KEY, command.getRequiredString(0));
+ sendReply(session);
+ }
+
+}
diff --git a/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/NlstCommandHandler.java b/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/NlstCommandHandler.java
new file mode 100644
index 0000000..b2f97d4
--- /dev/null
+++ b/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/NlstCommandHandler.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.stub.command;
+
+import org.mockftpserver.core.command.Command;
+import org.mockftpserver.core.command.CommandHandler;
+import org.mockftpserver.core.command.InvocationRecord;
+import org.mockftpserver.core.session.Session;
+
+/**
+ * CommandHandler for the NLST command. Return the configured directory listing on the data
+ * connection, along with two replies on the control connection: a reply code of 150 and
+ * another of 226. By default, return an empty directory listing. You can customize the
+ * returned directory listing by setting the <code>directoryListing</code> property.
+ * <p>
+ * Each invocation record stored by this CommandHandler includes the following data element key/values:
+ * <ul>
+ * <li>{@link #PATHNAME_KEY} ("pathname") - the pathname of the directory (or file) submitted on the
+ * invocation (the first command parameter); this parameter is optional, so the value may be null.
+ * </ul>
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public final class NlstCommandHandler extends AbstractStubDataCommandHandler implements CommandHandler {
+
+ public static final String PATHNAME_KEY = "pathname";
+
+ private String directoryListing = "";
+
+ /**
+ * @see org.mockftpserver.stub.command.AbstractStubDataCommandHandler#beforeProcessData(org.mockftpserver.core.command.Command, org.mockftpserver.core.session.Session, org.mockftpserver.core.command.InvocationRecord)
+ */
+ protected void beforeProcessData(Command command, Session session, InvocationRecord invocationRecord) throws Exception {
+ invocationRecord.set(PATHNAME_KEY, command.getOptionalString(0));
+ }
+
+ /**
+ * @see org.mockftpserver.stub.command.AbstractStubDataCommandHandler#processData(org.mockftpserver.core.command.Command, org.mockftpserver.core.session.Session, org.mockftpserver.core.command.InvocationRecord)
+ */
+ protected void processData(Command command, Session session, InvocationRecord invocationRecord) {
+ session.sendData(directoryListing.getBytes(), directoryListing.length());
+ }
+
+ /**
+ * Set the contents of the directoryListing to send back on the data connection for this command.
+ * The passed-in value is trimmed automatically.
+ * @param directoryListing - the directoryListing to set
+ */
+ public void setDirectoryListing(String directoryListing) {
+ this.directoryListing = directoryListing.trim();
+ }
+
+}
diff --git a/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/NoopCommandHandler.java b/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/NoopCommandHandler.java
new file mode 100644
index 0000000..c7f86d5
--- /dev/null
+++ b/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/NoopCommandHandler.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.stub.command;
+
+import org.mockftpserver.core.command.Command;
+import org.mockftpserver.core.command.CommandHandler;
+import org.mockftpserver.core.command.InvocationRecord;
+import org.mockftpserver.core.command.ReplyCodes;
+import org.mockftpserver.core.session.Session;
+
+/**
+ * CommandHandler for the NOOP command. Return a reply code of 200.
+ * <p>
+ * Each invocation record stored by this CommandHandler contains no data elements.
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public final class NoopCommandHandler extends AbstractStubCommandHandler implements CommandHandler {
+
+ /**
+ * Constructor. Initialize the replyCode.
+ */
+ public NoopCommandHandler() {
+ setReplyCode(ReplyCodes.NOOP_OK);
+ }
+
+ /**
+ * @see org.mockftpserver.core.command.CommandHandler#handleCommand(Command, Session, InvocationRecord)
+ */
+ public void handleCommand(Command command, Session session, InvocationRecord invocationRecord) {
+ sendReply(session);
+ }
+
+}
diff --git a/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/PassCommandHandler.java b/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/PassCommandHandler.java
new file mode 100644
index 0000000..c380539
--- /dev/null
+++ b/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/PassCommandHandler.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.stub.command;
+
+import org.mockftpserver.core.command.Command;
+import org.mockftpserver.core.command.CommandHandler;
+import org.mockftpserver.core.command.InvocationRecord;
+import org.mockftpserver.core.command.ReplyCodes;
+import org.mockftpserver.core.session.Session;
+
+/**
+ * CommandHandler for the PASS (Password) command. Send back a reply code of 230.
+ * <p>
+ * Each invocation record stored by this CommandHandler includes the following data element key/values:
+ * <ul>
+ * <li>"password" - the password submitted on the invocation (the first command parameter)
+ * </ul>
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public final class PassCommandHandler extends AbstractStubCommandHandler implements CommandHandler {
+
+ public static final String PASSWORD_KEY = "password";
+
+ /**
+ * Constructor. Initialize the replyCode.
+ */
+ public PassCommandHandler() {
+ setReplyCode(ReplyCodes.PASS_OK);
+ }
+
+ /**
+ * @see org.mockftpserver.core.command.CommandHandler#handleCommand(Command, Session, InvocationRecord)
+ */
+ public void handleCommand(Command command, Session session, InvocationRecord invocationRecord) {
+ invocationRecord.set(PASSWORD_KEY, command.getRequiredString(0));
+ sendReply(session);
+ }
+
+}
diff --git a/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/PasvCommandHandler.java b/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/PasvCommandHandler.java
new file mode 100644
index 0000000..1d460d6
--- /dev/null
+++ b/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/PasvCommandHandler.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.stub.command;
+
+import java.io.IOException;
+import java.net.InetAddress;
+
+
+import org.apache.log4j.Logger;
+import org.mockftpserver.core.command.Command;
+import org.mockftpserver.core.command.CommandHandler;
+import org.mockftpserver.core.command.InvocationRecord;
+import org.mockftpserver.core.command.ReplyCodes;
+import org.mockftpserver.core.session.Session;
+import org.mockftpserver.core.util.Assert;
+
+/**
+ * CommandHandler for the PASV (Passove Mode) command. Request the Session to switch to passive
+ * data connection mode. Return a reply code of 227, along with response text of the form:
+ * "<i>Entering Passive Mode. (h1,h2,h3,h4,p1,p2)</i>", where <i>h1..h4</i> are the 4
+ * bytes of the 32-bit internet host address of the server, and <i>p1..p2</i> are the 2
+ * bytes of the 16-bit TCP port address of the data connection on the server to which
+ * the client must connect. See RFC959 for more information.
+ * <p>
+ * Each invocation record stored by this CommandHandler contains no data elements.
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public final class PasvCommandHandler extends AbstractStubCommandHandler implements CommandHandler {
+
+ private static final Logger LOG = Logger.getLogger(PasvCommandHandler.class);
+
+ /**
+ * Constructor. Initialize the replyCode.
+ */
+ public PasvCommandHandler() {
+ setReplyCode(ReplyCodes.PASV_OK);
+ }
+
+ /**
+ * @throws IOException
+ * @see org.mockftpserver.core.command.CommandHandler#handleCommand(Command, Session, InvocationRecord)
+ */
+ public void handleCommand(Command command, Session session, InvocationRecord invocationRecord)
+ throws IOException {
+
+ int port = session.switchToPassiveMode();
+ InetAddress server = session.getServerHost();
+
+ Assert.isTrue(port > -1, "The server-side port is invalid: " + port);
+ LOG.debug("server=" + server + " port=" + port);
+ String hostAndPort = "(" + convertHostAndPortToStringOfBytes(server, port) + ")";
+
+ sendReply(session, hostAndPort);
+ }
+
+ /**
+ * Convert the InetAddess and port number to a comma-delimited list of byte values,
+ * suitable for the response String from the PASV command.
+ * @param host - the InetAddress
+ * @param port - the port number
+ * @return the comma-delimited list of byte values, e.g., "196,168,44,55,23,77"
+ */
+ static String convertHostAndPortToStringOfBytes(InetAddress host, int port) {
+ StringBuffer buffer = new StringBuffer();
+ byte[] address = host.getAddress();
+ for (int i = 0; i < address.length; i++) {
+ int positiveValue = (address[i] >=0) ? address[i] : 256 + address[i];
+ buffer.append(positiveValue);
+ buffer.append(",");
+ }
+ int p1 = port >> 8;
+ int p2 = port % 256;
+ buffer.append(String.valueOf(p1));
+ buffer.append(",");
+ buffer.append(String.valueOf(p2));
+ return buffer.toString();
+ }
+
+}
diff --git a/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/PortCommandHandler.java b/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/PortCommandHandler.java
new file mode 100644
index 0000000..f815fab
--- /dev/null
+++ b/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/PortCommandHandler.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.stub.command;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.Arrays;
+
+import org.apache.log4j.Logger;
+import org.mockftpserver.core.command.Command;
+import org.mockftpserver.core.command.CommandHandler;
+import org.mockftpserver.core.command.InvocationRecord;
+import org.mockftpserver.core.command.ReplyCodes;
+import org.mockftpserver.core.session.Session;
+import org.mockftpserver.core.util.Assert;
+import org.mockftpserver.core.util.AssertFailedException;
+
+/**
+ * CommandHandler for the PORT command. Send back a reply code of 200.
+ * <p>
+ * Each invocation record stored by this CommandHandler includes the following data element key/values:
+ * <ul>
+ * <li>{@link #HOST_KEY} ("host") - the client data host (InetAddress) submitted on the invocation (from parameters 1-4)
+ * <li>{@link #PORT_KEY} ("port") - the port number (Integer) submitted on the invocation (from parameter 5-6)
+ * </ul>
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public final class PortCommandHandler extends AbstractStubCommandHandler implements CommandHandler {
+
+ public static final String HOST_KEY = "host";
+ public static final String PORT_KEY = "port";
+ private static final Logger LOG = Logger.getLogger(PortCommandHandler.class);
+
+ /**
+ * Constructor. Initialize the replyCode.
+ */
+ public PortCommandHandler() {
+ setReplyCode(ReplyCodes.PORT_OK);
+ }
+
+ /**
+ * @throws UnknownHostException
+ * @throws UnknownHostException
+ * @see org.mockftpserver.core.command.CommandHandler#handleCommand(Command, Session, InvocationRecord)
+ */
+ public void handleCommand(Command command, Session session, InvocationRecord invocationRecord) throws UnknownHostException {
+ InetAddress host = parseHost(command.getParameters());
+ int port = parsePortNumber(command.getParameters());
+ LOG.debug("host=" + host + " port=" + port);
+ session.setClientDataHost(host);
+ session.setClientDataPort(port);
+ invocationRecord.set(HOST_KEY, host);
+ invocationRecord.set(PORT_KEY, new Integer(port));
+ sendReply(session);
+ }
+
+ /**
+ * Parse a 32-bit IP address from the String[] of FTP command parameters.
+ *
+ * @param parameters - the String[] of command parameters. It is the concatenation
+ * of a 32-bit internet host address and a 16-bit TCP port address. This address
+ * information is broken into 8-bit fields and the value of each field is encoded
+ * as a separate parameter whose value is a decimal number (in character string
+ * representation). Thus, the six parameters for the port command would be:
+ * h1,h2,h3,h4,p1,p2
+ * where h1 is the high order 8 bits of the internet host address, and p1 is the
+ * high order 8 bits of the port number.
+ *
+ * @return the InetAddres representing the host parsed from the parameters
+ *
+ * @throws AssertFailedException - if parameters is null or contains an insufficient number of elements
+ * @throws NumberFormatException - if one of the parameters does not contain a parsable integer
+ * @throws UnknownHostException
+ */
+ static InetAddress parseHost(String[] parameters) throws UnknownHostException {
+ verifySufficientParameters(parameters);
+
+ byte host1 = parseByte(parameters[0]);
+ byte host2 = parseByte(parameters[1]);
+ byte host3 = parseByte(parameters[2]);
+ byte host4 = parseByte(parameters[3]);
+
+ byte[] address = { host1, host2, host3, host4 };
+ InetAddress inetAddress = InetAddress.getByAddress(address);
+
+ return inetAddress;
+ }
+
+ /**
+ * Parse a 16-bit port number from the String[] of FTP command parameters.
+ *
+ * @param parameters - the String[] of command parameters. It is the concatenation
+ * of a 32-bit internet host address and a 16-bit TCP port address. This address
+ * information is broken into 8-bit fields and the value of each field is encoded
+ * as a separate parameter whose value is a decimal number (in character string
+ * representation). Thus, the six parameters for the port command would be:
+ * h1,h2,h3,h4,p1,p2
+ * where h1 is the high order 8 bits of the internet host address, and p1 is the
+ * high order 8 bits of the port number.
+ *
+ * @return the port number parsed from the parameters
+ *
+ * @throws AssertFailedException - if parameters is null or contains an insufficient number of elements
+ * @throws NumberFormatException - if one of the parameters does not contain a parsable integer
+ */
+ static int parsePortNumber(String[] parameters) {
+ verifySufficientParameters(parameters);
+
+ int port1 = Integer.parseInt(parameters[4]);
+ int port2 = Integer.parseInt(parameters[5]);
+ int port = (port1 << 8) + port2;
+
+ return port;
+ }
+
+ /**
+ * Verify that the parameters is not null and contains the required number of elements
+ * @param parameters - the String[] of command parameters
+ * @throws AssertFailedException - if parameters is null or contains an insufficient number of elements
+ */
+ private static void verifySufficientParameters(String[] parameters) {
+ Assert.notNull(parameters, "parameters");
+ Assert.isTrue(parameters.length >= 6, "The PORT command must contain least be 6 parameters: " + Arrays.asList(parameters));
+ }
+
+ /**
+ * Parse the specified String as an unsigned decimal byte value (i.e., 0..255). We can't just use
+ * Byte.parseByte(string) because that parses the string as a signed byte.
+ *
+ * @param string - the String containing the decimal byte representation to be parsed
+ * @return the byte value
+ */
+ private static byte parseByte(String string) {
+ return (byte) (0xFF & Short.parseShort(string));
+ }
+
+}
diff --git a/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/PwdCommandHandler.java b/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/PwdCommandHandler.java
new file mode 100644
index 0000000..ba5d519
--- /dev/null
+++ b/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/PwdCommandHandler.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.stub.command;
+
+import org.mockftpserver.core.command.Command;
+import org.mockftpserver.core.command.CommandHandler;
+import org.mockftpserver.core.command.InvocationRecord;
+import org.mockftpserver.core.command.ReplyCodes;
+import org.mockftpserver.core.session.Session;
+
+/**
+ * CommandHandler for the PWD (Print Working Directory) and XPWD commands. By default, return
+ * an empty directory name, along with a reply code of 257. You can customize the returned
+ * directory name by setting the <code>directory</code> property.
+ * <p>
+ * Each invocation record stored by this CommandHandler contains no data elements.
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public final class PwdCommandHandler extends AbstractStubCommandHandler implements CommandHandler {
+
+ private String directory = "";
+
+ /**
+ * Constructor. Initialize the replyCode.
+ */
+ public PwdCommandHandler() {
+ setReplyCode(ReplyCodes.PWD_OK);
+ }
+
+ /**
+ * @see org.mockftpserver.core.command.CommandHandler#handleCommand(Command, Session, InvocationRecord)
+ */
+ public void handleCommand(Command command, Session session, InvocationRecord invocationRecord) {
+ sendReply(session, quotes(directory));
+ }
+
+ /**
+ * Set the directory String to be returned by this command
+ * @param directory - the directory
+ */
+ public void setDirectory(String response) {
+ this.directory = response;
+ }
+
+}
diff --git a/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/QuitCommandHandler.java b/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/QuitCommandHandler.java
new file mode 100644
index 0000000..6a90e4a
--- /dev/null
+++ b/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/QuitCommandHandler.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.stub.command;
+
+import org.mockftpserver.core.command.Command;
+import org.mockftpserver.core.command.CommandHandler;
+import org.mockftpserver.core.command.InvocationRecord;
+import org.mockftpserver.core.command.ReplyCodes;
+import org.mockftpserver.core.session.Session;
+
+/**
+ * CommandHandler for the QUIT command. Return a reply code of 221.
+ * <p>
+ * Each invocation record stored by this CommandHandler contains no data elements.
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public final class QuitCommandHandler extends AbstractStubCommandHandler implements CommandHandler {
+
+ /**
+ * Constructor. Initialize the replyCode.
+ */
+ public QuitCommandHandler() {
+ setReplyCode(ReplyCodes.QUIT_OK);
+ }
+
+ /**
+ * @see org.mockftpserver.core.command.CommandHandler#handleCommand(Command, Session, InvocationRecord)
+ */
+ public void handleCommand(Command command, Session session, InvocationRecord invocationRecord) {
+ sendReply(session);
+ session.close();
+ }
+
+}
diff --git a/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/ReinCommandHandler.java b/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/ReinCommandHandler.java
new file mode 100644
index 0000000..8dc5282
--- /dev/null
+++ b/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/ReinCommandHandler.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.stub.command;
+
+import org.mockftpserver.core.command.Command;
+import org.mockftpserver.core.command.CommandHandler;
+import org.mockftpserver.core.command.InvocationRecord;
+import org.mockftpserver.core.command.ReplyCodes;
+import org.mockftpserver.core.session.Session;
+
+/**
+/**
+ * CommandHandler for the REIN (Reinitialize) command. Send back a reply code of 220.
+ * <p>
+ * Each invocation record stored by this CommandHandler contains no data elements.
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public final class ReinCommandHandler extends AbstractStubCommandHandler implements CommandHandler {
+
+ /**
+ * Constructor. Initialize the replyCode.
+ */
+ public ReinCommandHandler() {
+ setReplyCode(ReplyCodes.REIN_OK);
+ }
+
+ /**
+ * @see org.mockftpserver.core.command.CommandHandler#handleCommand(Command, Session, InvocationRecord)
+ */
+ public void handleCommand(Command command, Session session, InvocationRecord invocationRecord) {
+ sendReply(session);
+ }
+
+}
diff --git a/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/RestCommandHandler.java b/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/RestCommandHandler.java
new file mode 100644
index 0000000..4b601f3
--- /dev/null
+++ b/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/RestCommandHandler.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.stub.command;
+
+import org.mockftpserver.core.command.Command;
+import org.mockftpserver.core.command.CommandHandler;
+import org.mockftpserver.core.command.InvocationRecord;
+import org.mockftpserver.core.command.ReplyCodes;
+import org.mockftpserver.core.session.Session;
+
+/**
+ * CommandHandler for the REST (Restart of interrupted transfer) command. Send back a reply code of 350.
+ * <p>
+ * Each invocation record stored by this CommandHandler includes the following data element key/values:
+ * <ul>
+ * <li>{@link #MARKER_KEY} ("marker") - the server marker submitted on the invocation (the first command parameter)
+ * </ul>
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public final class RestCommandHandler extends AbstractStubCommandHandler implements CommandHandler {
+
+ public static final String MARKER_KEY = "marker";
+
+ /**
+ * Constructor. Initialize the replyCode.
+ */
+ public RestCommandHandler() {
+ setReplyCode(ReplyCodes.REST_OK);
+ }
+
+ /**
+ * @see org.mockftpserver.core.command.CommandHandler#handleCommand(Command, Session, InvocationRecord)
+ */
+ public void handleCommand(Command command, Session session, InvocationRecord invocationRecord) {
+ String marker = command.getRequiredString(0);
+ invocationRecord.set(MARKER_KEY, marker);
+ sendReply(session, marker);
+ }
+
+}
diff --git a/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/RetrCommandHandler.java b/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/RetrCommandHandler.java
new file mode 100644
index 0000000..16192cc
--- /dev/null
+++ b/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/RetrCommandHandler.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.stub.command;
+
+import org.apache.log4j.Logger;
+import org.mockftpserver.core.command.Command;
+import org.mockftpserver.core.command.CommandHandler;
+import org.mockftpserver.core.command.InvocationRecord;
+import org.mockftpserver.core.session.Session;
+import org.mockftpserver.core.util.Assert;
+
+/**
+ * CommandHandler for the RETR (Retrieve) command. Return the configured file contents on the data
+ * connection, along with two replies on the control connection: a reply code of 150 and
+ * another of 226. By default, return an empty file (i.e., a zero-length byte[]). You can
+ * customize the returned file contents by setting the <code>fileContents</code> property,
+ * specified either as a String or as a byte array.
+ * <p>
+ * Each invocation record stored by this CommandHandler includes the following data element key/values:
+ * <ul>
+ * <li>{@link #PATHNAME_KEY} ("pathname") - the pathname of the file submitted on the invocation (the first command parameter)
+ * </ul>
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public final class RetrCommandHandler extends AbstractStubDataCommandHandler implements CommandHandler {
+
+ private static final Logger LOG = Logger.getLogger(RetrCommandHandler.class);
+ public static final String PATHNAME_KEY = "pathname";
+
+ private byte[] fileContents = new byte[0];
+
+ /**
+ * Create new uninitialized instance
+ */
+ public RetrCommandHandler() {
+ }
+
+ /**
+ * Create new instance using the specified fileContents
+ * @param fileContents - the file contents
+ * @throws AssertFailedException - if the fileContents is null
+ */
+ public RetrCommandHandler(String fileContents) {
+ setFileContents(fileContents);
+ }
+
+ /**
+ * Create new instance using the specified fileContents
+ * @param fileContents - the file contents
+ * @throws AssertFailedException - if the fileContents is null
+ */
+ public RetrCommandHandler(byte[] fileContents) {
+ setFileContents(fileContents);
+ }
+
+ /**
+ * @see org.mockftpserver.stub.command.AbstractStubDataCommandHandler#beforeProcessData(org.mockftpserver.core.command.Command, org.mockftpserver.core.session.Session, org.mockftpserver.core.command.InvocationRecord)
+ */
+ protected void beforeProcessData(Command command, Session session, InvocationRecord invocationRecord) throws Exception {
+ String filename = command.getRequiredString(0);
+ invocationRecord.set(PATHNAME_KEY, filename);
+ }
+
+ /**
+ * @see org.mockftpserver.stub.command.AbstractStubDataCommandHandler#processData(org.mockftpserver.core.command.Command, org.mockftpserver.core.session.Session, org.mockftpserver.core.command.InvocationRecord)
+ */
+ protected void processData(Command command, Session session, InvocationRecord invocationRecord) {
+ LOG.info("Sending " + fileContents.length + " bytes");
+ session.sendData(fileContents, fileContents.length);
+ }
+
+ /**
+ * Set the file contents to return from subsequent command invocations
+ * @param fileContents - the fileContents to set
+ * @throws AssertFailedException - if the fileContents is null
+ */
+ public void setFileContents(String fileContents) {
+ Assert.notNull(fileContents, "fileContents");
+ setFileContents(fileContents.getBytes());
+ }
+
+ /**
+ * Set the file contents to return from subsequent command invocations
+ * @param fileContents - the file contents
+ * @throws AssertFailedException - if the fileContents is null
+ */
+ public void setFileContents(byte[] fileContents) {
+ Assert.notNull(fileContents, "fileContents");
+ this.fileContents = fileContents;
+ }
+
+}
diff --git a/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/RmdCommandHandler.java b/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/RmdCommandHandler.java
new file mode 100644
index 0000000..4d8fe66
--- /dev/null
+++ b/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/RmdCommandHandler.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.stub.command;
+
+import org.mockftpserver.core.command.Command;
+import org.mockftpserver.core.command.CommandHandler;
+import org.mockftpserver.core.command.InvocationRecord;
+import org.mockftpserver.core.command.ReplyCodes;
+import org.mockftpserver.core.session.Session;
+
+/**
+ * CommandHandler for the RMD (Remove Working Directory) command. Send back a reply code of 250.
+ * <p>
+ * Each invocation record stored by this CommandHandler includes the following data element key/values:
+ * <ul>
+ * <li>{@link #PATHNAME_KEY} ("pathname") - the pathname of the directory submitted on the invocation (the first command parameter)
+ * </ul>
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public final class RmdCommandHandler extends AbstractStubCommandHandler implements CommandHandler {
+
+ public static final String PATHNAME_KEY = "pathname";
+
+ /**
+ * Constructor. Initialize the replyCode.
+ */
+ public RmdCommandHandler() {
+ setReplyCode(ReplyCodes.RMD_OK);
+ }
+
+ /**
+ * @see org.mockftpserver.core.command.CommandHandler#handleCommand(Command, Session, InvocationRecord)
+ */
+ public void handleCommand(Command command, Session session, InvocationRecord invocationRecord) {
+ invocationRecord.set(PATHNAME_KEY, command.getRequiredString(0));
+ sendReply(session);
+ }
+
+}
diff --git a/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/RnfrCommandHandler.java b/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/RnfrCommandHandler.java
new file mode 100644
index 0000000..650cc16
--- /dev/null
+++ b/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/RnfrCommandHandler.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.stub.command;
+
+import org.mockftpserver.core.command.Command;
+import org.mockftpserver.core.command.CommandHandler;
+import org.mockftpserver.core.command.InvocationRecord;
+import org.mockftpserver.core.command.ReplyCodes;
+import org.mockftpserver.core.session.Session;
+
+/**
+ * CommandHandler for the RNFR (Rename From) command. Send back a reply code of 350.
+ * <p>
+ * Each invocation record stored by this CommandHandler includes the following data element key/values:
+ * <ul>
+ * <li>{@link #PATHNAME_KEY} ("pathname") - the pathname of the file submitted on the invocation (the first command parameter)
+ * </ul>
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public final class RnfrCommandHandler extends AbstractStubCommandHandler implements CommandHandler {
+
+ public static final String PATHNAME_KEY = "pathname";
+
+ /**
+ * Constructor. Initialize the replyCode.
+ */
+ public RnfrCommandHandler() {
+ setReplyCode(ReplyCodes.RNFR_OK);
+ }
+
+ /**
+ * @see org.mockftpserver.core.command.CommandHandler#handleCommand(Command, Session, InvocationRecord)
+ */
+ public void handleCommand(Command command, Session session, InvocationRecord invocationRecord) {
+ invocationRecord.set(PATHNAME_KEY, command.getRequiredString(0));
+ sendReply(session);
+ }
+
+}
diff --git a/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/RntoCommandHandler.java b/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/RntoCommandHandler.java
new file mode 100644
index 0000000..19b8f5e
--- /dev/null
+++ b/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/RntoCommandHandler.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.stub.command;
+
+import org.mockftpserver.core.command.Command;
+import org.mockftpserver.core.command.CommandHandler;
+import org.mockftpserver.core.command.InvocationRecord;
+import org.mockftpserver.core.command.ReplyCodes;
+import org.mockftpserver.core.session.Session;
+
+/**
+ * CommandHandler for the RNTO (Rename To) command. Send back a reply code of 250.
+ * <p>
+ * Each invocation record stored by this CommandHandler includes the following data element key/values:
+ * <ul>
+ * <li>{@link #PATHNAME_KEY} ("pathname") - the pathname of the file submitted on the invocation (the first command parameter)
+ * </ul>
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public final class RntoCommandHandler extends AbstractStubCommandHandler implements CommandHandler {
+
+ public static final String PATHNAME_KEY = "pathname";
+
+ /**
+ * Constructor. Initialize the replyCode.
+ */
+ public RntoCommandHandler() {
+ setReplyCode(ReplyCodes.RNTO_OK);
+ }
+
+ /**
+ * @see org.mockftpserver.core.command.CommandHandler#handleCommand(Command, Session, InvocationRecord)
+ */
+ public void handleCommand(Command command, Session session, InvocationRecord invocationRecord) {
+ invocationRecord.set(PATHNAME_KEY, command.getRequiredString(0));
+ sendReply(session);
+ }
+
+}
diff --git a/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/SiteCommandHandler.java b/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/SiteCommandHandler.java
new file mode 100644
index 0000000..f031adc
--- /dev/null
+++ b/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/SiteCommandHandler.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.stub.command;
+
+import org.mockftpserver.core.command.Command;
+import org.mockftpserver.core.command.CommandHandler;
+import org.mockftpserver.core.command.InvocationRecord;
+import org.mockftpserver.core.command.ReplyCodes;
+import org.mockftpserver.core.session.Session;
+
+/**
+ * CommandHandler for the SITE (Site Parameters) command. Send back a reply code of 200.
+ * <p>
+ * Each invocation record stored by this CommandHandler includes the following data element key/values:
+ * <ul>
+ * <li>{@link #PARAMETERS_KEY} ("parameters") - the site parameters submitted on the invocation (the first command parameter)
+ * </ul>
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public final class SiteCommandHandler extends AbstractStubCommandHandler implements CommandHandler {
+
+ public static final String PARAMETERS_KEY = "parameters";
+
+ /**
+ * Constructor. Initiate the replyCode.
+ */
+ public SiteCommandHandler() {
+ setReplyCode(ReplyCodes.SITE_OK);
+ }
+
+ /**
+ * @see org.mockftpserver.core.command.CommandHandler#handleCommand(Command, Session, InvocationRecord)
+ */
+ public void handleCommand(Command command, Session session, InvocationRecord invocationRecord) {
+ invocationRecord.set(PARAMETERS_KEY, command.getRequiredString(0));
+ sendReply(session);
+ }
+
+}
diff --git a/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/SmntCommandHandler.java b/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/SmntCommandHandler.java
new file mode 100644
index 0000000..4d603f7
--- /dev/null
+++ b/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/SmntCommandHandler.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.stub.command;
+
+import org.mockftpserver.core.command.Command;
+import org.mockftpserver.core.command.CommandHandler;
+import org.mockftpserver.core.command.InvocationRecord;
+import org.mockftpserver.core.command.ReplyCodes;
+import org.mockftpserver.core.session.Session;
+
+/**
+ * CommandHandler for the SMNT (Structure Mount) command. Send back a reply code of 250.
+ * <p>
+ * Each invocation record stored by this CommandHandler includes the following data element key/values:
+ * <ul>
+ * <li>{@link #PATHNAME_KEY} ("pathname") - the pathname of the directory submitted on the invocation (the first command parameter)
+ * </ul>
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public final class SmntCommandHandler extends AbstractStubCommandHandler implements CommandHandler {
+
+ public static final String PATHNAME_KEY = "pathname";
+
+ /**
+ * Constructor. Initiate the replyCode.
+ */
+ public SmntCommandHandler() {
+ setReplyCode(ReplyCodes.SMNT_OK);
+ }
+
+ /**
+ * @see org.mockftpserver.core.command.CommandHandler#handleCommand(Command, Session, InvocationRecord)
+ */
+ public void handleCommand(Command command, Session session, InvocationRecord invocationRecord) {
+ invocationRecord.set(PATHNAME_KEY, command.getRequiredString(0));
+ sendReply(session);
+ }
+
+}
diff --git a/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/StatCommandHandler.java b/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/StatCommandHandler.java
new file mode 100644
index 0000000..916a679
--- /dev/null
+++ b/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/StatCommandHandler.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.stub.command;
+
+import org.mockftpserver.core.command.Command;
+import org.mockftpserver.core.command.CommandHandler;
+import org.mockftpserver.core.command.InvocationRecord;
+import org.mockftpserver.core.command.ReplyCodes;
+import org.mockftpserver.core.session.Session;
+
+/**
+ * CommandHandler for the STAT (Status) command. By default, return empty status information,
+ * along with a reply code of 211 if no pathname parameter is specified or 213 if a
+ * pathname is specified. You can customize the returned status information by setting
+ * the <code>status</code> property.
+ * <p>
+ * Each invocation record stored by this CommandHandler includes the following data element key/values:
+ * <ul>
+ * <li>{@link #PATHNAME_KEY} ("pathname") - the pathname of the directory (or file) submitted on the
+ * invocation (the first command parameter); this parameter is optional, so the value may be null.
+ * </ul>
+ *
+ * @see SystCommandHandler
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public final class StatCommandHandler extends AbstractStubCommandHandler implements CommandHandler {
+
+ public static final String PATHNAME_KEY = "pathname";
+
+ private String status = "";
+
+ /**
+ * Constructor.
+ */
+ public StatCommandHandler() {
+ // Do not initialize replyCode -- will be set dynamically
+ }
+
+ /**
+ * @see org.mockftpserver.core.command.CommandHandler#handleCommand(Command, Session, InvocationRecord)
+ */
+ public void handleCommand(Command command, Session session, InvocationRecord invocationRecord) {
+ String pathname = command.getOptionalString(0);
+ invocationRecord.set(PATHNAME_KEY, pathname);
+
+ // Only use dynamic reply code if the replyCode property was NOT explicitly set
+ if (replyCode == 0) {
+ int code = (pathname == null) ? ReplyCodes.STAT_SYSTEM_OK : ReplyCodes.STAT_FILE_OK;
+ sendReply(session, code, replyMessageKey, replyText, new String[] { status });
+ }
+ else {
+ sendReply(session, status);
+ }
+ }
+
+ /**
+ * Set the contents of the status to send back as the reply text for this command
+ * @param status - the status
+ */
+ public void setStatus(String status) {
+ this.status = status;
+ }
+
+}
diff --git a/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/StorCommandHandler.java b/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/StorCommandHandler.java
new file mode 100644
index 0000000..af845da
--- /dev/null
+++ b/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/StorCommandHandler.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.stub.command;
+
+import org.apache.log4j.Logger;
+import org.mockftpserver.core.command.Command;
+import org.mockftpserver.core.command.CommandHandler;
+import org.mockftpserver.core.command.InvocationRecord;
+import org.mockftpserver.core.session.Session;
+
+/**
+ * CommandHandler for the STOR (Store) command. Send back two replies on the control connection: a
+ * reply code of 150 and another of 226.
+ * <p>
+ * Each invocation record stored by this CommandHandler includes the following data element key/values:
+ * <ul>
+ * <li>{@link #PATHNAME_KEY} ("pathname") - the pathname of the directory submitted on the invocation (the first command parameter)
+ * <li>{@link #FILE_CONTENTS_KEY} ("fileContents") - the file contents (<code>byte[]</code>) sent on the data connection
+ * </ul>
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public final class StorCommandHandler extends AbstractStubDataCommandHandler implements CommandHandler {
+
+ public static final String PATHNAME_KEY = "pathname";
+ public static final String FILE_CONTENTS_KEY = "filecontents";
+
+ private static final Logger LOG = Logger.getLogger(StorCommandHandler.class);
+
+ /**
+ * @see org.mockftpserver.stub.command.AbstractStubDataCommandHandler#beforeProcessData(org.mockftpserver.core.command.Command, org.mockftpserver.core.session.Session, org.mockftpserver.core.command.InvocationRecord)
+ */
+ protected void beforeProcessData(Command command, Session session, InvocationRecord invocationRecord) throws Exception {
+ String filename = command.getRequiredString(0);
+ invocationRecord.set(PATHNAME_KEY, filename);
+ }
+
+ /**
+ * @see org.mockftpserver.stub.command.AbstractStubDataCommandHandler#processData(org.mockftpserver.core.command.Command, org.mockftpserver.core.session.Session, org.mockftpserver.core.command.InvocationRecord)
+ */
+ protected void processData(Command command, Session session, InvocationRecord invocationRecord) {
+ byte[] data = session.readData();
+ LOG.info("Received " + data.length + " bytes");
+ LOG.trace("Received data [" + new String(data) + "]");
+ invocationRecord.set(FILE_CONTENTS_KEY, data);
+ }
+
+}
diff --git a/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/StouCommandHandler.java b/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/StouCommandHandler.java
new file mode 100644
index 0000000..453719e
--- /dev/null
+++ b/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/StouCommandHandler.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.stub.command;
+
+import org.apache.log4j.Logger;
+import org.mockftpserver.core.command.Command;
+import org.mockftpserver.core.command.CommandHandler;
+import org.mockftpserver.core.command.InvocationRecord;
+import org.mockftpserver.core.command.ReplyCodes;
+import org.mockftpserver.core.session.Session;
+
+/**
+ * CommandHandler for the STOU (Store Unique) command. Send back two replies on the control connection: a
+ * reply code of 150 and another of 226. The text accompanying the final reply (226) is the
+ * unique filename, which is "" by default. You can customize the returned filename by setting
+ * the <code>filename</code> property.
+ * <p>
+ * Each invocation record stored by this CommandHandler includes the following data element key/values:
+ * <ul>
+ * <li>{@link #FILE_CONTENTS_KEY} ("fileContents") - the file contents (<code>byte[]</code>) sent on the data connection
+ * </ul>
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public final class StouCommandHandler extends AbstractStubDataCommandHandler implements CommandHandler {
+
+ public static final String FILE_CONTENTS_KEY = "filecontents";
+ private static final String FINAL_REPLY_TEXT_KEY = "226.WithFilename";
+
+ private static final Logger LOG = Logger.getLogger(StouCommandHandler.class);
+
+ private String filename = "";
+
+ /**
+ * @see org.mockftpserver.stub.command.AbstractStubDataCommandHandler#processData(org.mockftpserver.core.command.Command, org.mockftpserver.core.session.Session, org.mockftpserver.core.command.InvocationRecord)
+ */
+ protected void processData(Command command, Session session, InvocationRecord invocationRecord) {
+ byte[] data = session.readData();
+ LOG.info("Received " + data.length + " bytes");
+ LOG.trace("Received data [" + new String(data) + "]");
+ invocationRecord.set(FILE_CONTENTS_KEY, data);
+ }
+
+ /**
+ * Override the default implementation to send a custom reply text that includes the STOU response filename
+ * @see org.mockftpserver.stub.command.AbstractStubDataCommandHandler#sendFinalReply(org.mockftpserver.core.session.Session)
+ */
+ protected void sendFinalReply(Session session) {
+ final String[] ARGS = { filename };
+ sendReply(session, ReplyCodes.SEND_DATA_FINAL_OK, FINAL_REPLY_TEXT_KEY, null, ARGS);
+ }
+
+ /**
+ * Set the filename returned with the final reply of the STOU command
+ * @param filename - the filename
+ */
+ public void setFilename(String filename) {
+ this.filename = filename;
+ }
+
+}
diff --git a/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/StruCommandHandler.java b/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/StruCommandHandler.java
new file mode 100644
index 0000000..6d2a28c
--- /dev/null
+++ b/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/StruCommandHandler.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.stub.command;
+
+import org.mockftpserver.core.command.Command;
+import org.mockftpserver.core.command.CommandHandler;
+import org.mockftpserver.core.command.InvocationRecord;
+import org.mockftpserver.core.command.ReplyCodes;
+import org.mockftpserver.core.session.Session;
+
+/**
+ * CommandHandler for the STRU (File Structure) command. Send back a reply code of 200.
+ * <p>
+ * Each invocation record stored by this CommandHandler includes the following data element key/values:
+ * <ul>
+ * <li>{@link #FILE_STRUCTURE_KEY} ("fileStructure") - the file structure code submitted on the invocation (the first command parameter)
+ * </ul>
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public final class StruCommandHandler extends AbstractStubCommandHandler implements CommandHandler {
+
+ public static final String FILE_STRUCTURE_KEY = "fileStructure";
+
+ /**
+ * Constructor. Initialize the replyCode.
+ */
+ public StruCommandHandler() {
+ setReplyCode(ReplyCodes.STRU_OK);
+ }
+
+ /**
+ * @see org.mockftpserver.core.command.CommandHandler#handleCommand(Command, Session, InvocationRecord)
+ */
+ public void handleCommand(Command command, Session session, InvocationRecord invocationRecord) {
+ invocationRecord.set(FILE_STRUCTURE_KEY, command.getRequiredString(0));
+ sendReply(session);
+ }
+
+}
diff --git a/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/SystCommandHandler.java b/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/SystCommandHandler.java
new file mode 100644
index 0000000..04a5b27
--- /dev/null
+++ b/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/SystCommandHandler.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.stub.command;
+
+import org.mockftpserver.core.command.Command;
+import org.mockftpserver.core.command.CommandHandler;
+import org.mockftpserver.core.command.InvocationRecord;
+import org.mockftpserver.core.command.ReplyCodes;
+import org.mockftpserver.core.session.Session;
+import org.mockftpserver.core.util.Assert;
+
+/**
+ * CommandHandler for the SYST (System) command. Send back a reply code of 215. By default,
+ * return "WINDOWS" as the system name. You can customize the returned name by
+ * setting the <code>systemName</code> property.
+ * <p>
+ * See the available system names listed in the Assigned Numbers document (RFC 943).
+ * <p>
+ * Each invocation record stored by this CommandHandler contains no data elements.
+
+ * @see http://www.ietf.org/rfc/rfc943
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public final class SystCommandHandler extends AbstractStubCommandHandler implements CommandHandler {
+
+ private String systemName = "WINDOWS";
+
+ /**
+ * Constructor. Initialize the replyCode.
+ */
+ public SystCommandHandler() {
+ setReplyCode(ReplyCodes.SYST_OK);
+ }
+
+ /**
+ * @see org.mockftpserver.core.command.CommandHandler#handleCommand(Command, Session, InvocationRecord)
+ */
+ public void handleCommand(Command command, Session session, InvocationRecord invocationRecord) {
+ sendReply(session, quotes(systemName));
+ }
+
+ /**
+ * Set the systemName String to be returned by this command
+ * @param systemName - the systemName
+ */
+ public void setSystemName(String systemName) {
+ Assert.notNull(systemName, "systemName");
+ this.systemName = systemName;
+ }
+
+}
diff --git a/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/TypeCommandHandler.java b/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/TypeCommandHandler.java
new file mode 100644
index 0000000..c624299
--- /dev/null
+++ b/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/TypeCommandHandler.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.stub.command;
+
+import org.apache.log4j.Logger;
+import org.mockftpserver.core.command.Command;
+import org.mockftpserver.core.command.CommandHandler;
+import org.mockftpserver.core.command.InvocationRecord;
+import org.mockftpserver.core.command.ReplyCodes;
+import org.mockftpserver.core.session.Session;
+
+/**
+ * CommandHandler for the TYPE command. Send back a reply code of 200.
+ * <p>
+ * Each invocation record stored by this CommandHandler includes the following data element key/values:
+ * <ul>
+ * <li>{@link #TYPE_INFO_KEY} ("typeInfo") - the type information submitted on the
+ * invocation, which is a String[2] containing the first two command parameter values.
+ * </ul>
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public final class TypeCommandHandler extends AbstractStubCommandHandler implements CommandHandler {
+
+ public static final String TYPE_INFO_KEY = "typeInfo";
+
+ private static final Logger LOG = Logger.getLogger(TypeCommandHandler.class);
+
+ /**
+ * Constructor. Initialize the replyCode.
+ */
+ public TypeCommandHandler() {
+ setReplyCode(ReplyCodes.TYPE_OK);
+ }
+
+ /**
+ * @see org.mockftpserver.core.command.CommandHandler#handleCommand(Command, Session, InvocationRecord)
+ */
+ public void handleCommand(Command command, Session session, InvocationRecord invocationRecord) {
+ LOG.debug("Processing TYPE: " + command);
+ String type = command.getRequiredString(0);
+ String format = command.getOptionalString(1);
+ invocationRecord.set(TYPE_INFO_KEY, new String[] {type, format});
+ sendReply(session);
+ }
+
+}
diff --git a/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/UserCommandHandler.java b/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/UserCommandHandler.java
new file mode 100644
index 0000000..64c1431
--- /dev/null
+++ b/tags/1.2.3/src/main/java/org/mockftpserver/stub/command/UserCommandHandler.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.stub.command;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.mockftpserver.core.command.Command;
+import org.mockftpserver.core.command.CommandHandler;
+import org.mockftpserver.core.command.InvocationRecord;
+import org.mockftpserver.core.command.ReplyCodes;
+import org.mockftpserver.core.session.Session;
+
+/**
+ * CommandHandler for the USER command. The <code>passwordRequired</code> property defaults to true,
+ * indicating that a password is required following the user name. If true, this command handler
+ * returns a reply of 331. If false, return a reply of 230.
+ * <p>
+ * Each invocation record stored by this CommandHandler includes the following data element key/values:
+ * <ul>
+ * <li>{@link #USERNAME_KEY} ("username") - the user name submitted on the invocation (the first command parameter)
+ * </ul>
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public final class UserCommandHandler extends AbstractStubCommandHandler implements CommandHandler {
+
+ public static final String USERNAME_KEY = "username";
+
+ private boolean passwordRequired = true;
+ private List usernames = new ArrayList();
+
+ /**
+ * Constructor.
+ */
+ public UserCommandHandler() {
+ // Do not initialize replyCode -- will be set dynamically
+ }
+
+ /**
+ * @see org.mockftpserver.core.command.CommandHandler#handleCommand(Command, Session, InvocationRecord)
+ */
+ public void handleCommand(Command command, Session session, InvocationRecord invocationRecord) {
+ usernames.add(command.getRequiredString(0));
+ invocationRecord.set(USERNAME_KEY, command.getRequiredString(0));
+
+ // Only use dynamic reply code if the replyCode property was NOT explicitly set
+ if (replyCode == 0) {
+ int code = (passwordRequired) ? ReplyCodes.USER_NEED_PASSWORD_OK : ReplyCodes.USER_LOGGED_IN_OK;
+ sendReply(session, code, replyMessageKey, replyText, null);
+ }
+ else {
+ sendReply(session);
+ }
+ }
+
+ /**
+ * Return true if a password is required at login. See {@link #setPasswordRequired(boolean)}.
+ * @return the passwordRequired flag
+ */
+ public boolean isPasswordRequired() {
+ return passwordRequired;
+ }
+
+ /**
+ * Set true to indicate that a password is required. If true, this command handler returns a reply
+ * of 331. If false, return a reply of 230.
+ * @param passwordRequired - is a password required for login
+ */
+ public void setPasswordRequired(boolean passwordRequired) {
+ this.passwordRequired = passwordRequired;
+ }
+
+}
diff --git a/tags/1.2.3/src/main/resources/ReplyText.properties b/tags/1.2.3/src/main/resources/ReplyText.properties
new file mode 100644
index 0000000..00b7e97
--- /dev/null
+++ b/tags/1.2.3/src/main/resources/ReplyText.properties
@@ -0,0 +1,62 @@
+# Copyright 2007 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# Mapping of reply code -> reply text
+110=Restart marker reply.
+120=Service ready in nnn minutes.
+125=Data connection already open; transfer starting.
+150=File status okay; about to open data connection.
+200=Command okay.
+202=Command not implemented, superfluous at this site.
+211={0}.
+212={0}.
+213={0}.
+214={0}.
+215={0} system type.
+220=Service ready for new user.
+221=Service closing control connection.
+225=Data connection open; no transfer in progress.
+226=Closing data connection. Requested file action successful.
+226.WithFilename=Closing data connection. Requested file action successful. Filename={0}.
+227=Entering Passive Mode {0}.
+230=User logged in, proceed.
+250=Requested file action okay, completed.
+257={0} created.
+331=User name okay, need password.
+332=Need account for login.
+350=Requested file action pending further information.
+421=Service not available, closing control connection.
+# This may be a reply to any command if the service knows it must shut down.
+425=Can't open data connection.
+426=Connection closed; transfer aborted.
+450=Requested file action not taken.
+# File unavailable (e.g., file busy).
+451=Requested action aborted: local error in processing.
+452=Requested action not taken.
+# Insufficient storage space in system.
+500=Syntax error, command unrecognized.
+# This may include errors such as command line too long.
+501=Syntax error in parameters or arguments.
+502=Command not implemented.
+503=Bad sequence of commands.
+504=Command not implemented for that parameter.
+530=Not logged in.
+532=Need account for storing files.
+550=Requested action not taken.
+# File unavailable (e.g., file not found, no access).
+551=Requested action aborted: page type unknown.
+552=Requested file action aborted.
+# Exceeded storage allocation (for current directory or dataset).
+553=Requested action not taken.
+# File name not allowed.
diff --git a/tags/1.2.3/src/site/apt/index.apt b/tags/1.2.3/src/site/apt/index.apt
new file mode 100644
index 0000000..6fb2886
--- /dev/null
+++ b/tags/1.2.3/src/site/apt/index.apt
@@ -0,0 +1,30 @@
+ --------------------------------------------------
+ Home
+ --------------------------------------------------
+
+MockFtpServer
+
+ The <<MockFtpServer>> project provides a mock/dummy FTP server implementation that can be very
+ useful for testing of FTP client code. It can be configured to return custom data or reply codes,
+ to simulate either success or failure scenarios. Expected command invocations can also be verified.
+
+ <<MockFtpServer>> is written in Java, and is ideally suited to testing Java code. But because
+ communication with the FTP server is across the network using sockets, it can be used to test FTP client
+ code written in any language.
+
+ The <<MockFtpServer>> project may one day provide multiple mock/dummy FTP server implementations,
+ at different levels of abstraction. Currently, however, <<StubFtpServer>> is the only one provided,
+ though others are being considered. <<StubFtpServer>> is a "stub" implementation of an FTP server.
+ See the {{{stubftpserver-getting-started.html}StubFtpServer Getting Started Guide}} for more information.
+
+ For projects built using {{{http://maven.apache.org/}Maven}}, <<MockFtpServer>> is now available
+ from the <<Maven Central Repository>>. Add a dependency to your POM like this:
+
+--------------------
+ <dependency>
+ <groupId>org.mockftpserver</groupId>
+ <artifactId>MockFtpServer</artifactId>
+ <version>1.2.1</version>
+ <scope>test</scope>
+ </dependency>
+-------------------- \ No newline at end of file
diff --git a/tags/1.2.3/src/site/apt/stubftpserver-commandhandlers.apt b/tags/1.2.3/src/site/apt/stubftpserver-commandhandlers.apt
new file mode 100644
index 0000000..3efd164
--- /dev/null
+++ b/tags/1.2.3/src/site/apt/stubftpserver-commandhandlers.apt
@@ -0,0 +1,89 @@
+ ------------------------------------------------------
+ StubFtpServer FTP Commands and CommandHandlers
+ ------------------------------------------------------
+
+StubFtpServer - FTP Commands and CommandHandlers
+
+ The following table lists the main FTP server commands with their corresponding FTP client commands,
+ and the <<StubFtpServer>> <CommandHandler> classes that implements support for the FTP server command.
+ See the Javadoc for each <CommandHandler> class for information on how to customize its behavior
+ through configuration, as well as what command invocation data is available.
+
+*------------------------*------------------------*------------------------------------------*
+| <<FTP Server Command>> | <<FTP Client Command>> | <<CommandHandler Class(es)>> |
+*------------------------*------------------------*------------------------------------------*
+| ABOR | -- | AborCommandHandler |
+*------------------------*------------------------*------------------------------------------*
+| ACCT | -- | AcctCommandHandler |
+*------------------------*------------------------*------------------------------------------*
+| ALLO | -- | AlloCommandHandler |
+*------------------------*------------------------*------------------------------------------*
+| APPE | APPEND | AppeCommandHandler |
+*------------------------*------------------------*------------------------------------------*
+| CDUP | -- | CdupCommandHandler |
+*------------------------*------------------------*------------------------------------------*
+| <CONNECT> (1) | OPEN | ConnectCommandHandler |
+*------------------------*------------------------*------------------------------------------*
+| CWD | CD | CwdCommandHandler |
+*------------------------*------------------------*------------------------------------------*
+| DELE | DELETE | DeleCommandHandler |
+*------------------------*------------------------*------------------------------------------*
+| HELP | REMOTEHELP | HelpCommandHandler |
+*------------------------*------------------------*------------------------------------------*
+| LIST | DIR / LS | ListCommandHandler |
+*------------------------*------------------------*------------------------------------------*
+| MKD | MKDIR | MkdCommandHandler |
+*------------------------*------------------------*------------------------------------------*
+| MODE | -- | ModeCommandHandler |
+*------------------------*------------------------*------------------------------------------*
+| NLST | -- | NlstCommandHandler |
+*------------------------*------------------------*------------------------------------------*
+| NOOP | -- | NoopCommandHandler |
+*------------------------*------------------------*------------------------------------------*
+| PASS | USER | PassCommandHandler |
+*------------------------*------------------------*------------------------------------------*
+| PASV | -- | PasvCommandHandler |
+*------------------------*------------------------*------------------------------------------*
+| PORT | -- | PortCommandHandler |
+*------------------------*------------------------*------------------------------------------*
+| PWD | PWD | PwdCommandHandler |
+*------------------------*------------------------*------------------------------------------*
+| QUIT | QUIT / BYE | QuitCommandHandler |
+*------------------------*------------------------*------------------------------------------*
+| REIN | -- | ReinCommandHandler |
+*------------------------*------------------------*------------------------------------------*
+| REST | -- | RestCommandHandler |
+*------------------------*------------------------*------------------------------------------*
+| RETR | GET / RECV | RetrCommandHandler |
+| | | FileRetrCommandHandler (2) |
+*------------------------*------------------------*------------------------------------------*
+| RMD | RMDIR | RmdCommandHandler |
+*------------------------*------------------------*------------------------------------------*
+| RNFR | RENAME | RnfrCommandHandler |
+*------------------------*------------------------*------------------------------------------*
+| RNTO | RENAME | RntoCommandHandler |
+*------------------------*------------------------*------------------------------------------*
+| SITE | -- | SiteCommandHandler |
+*------------------------*------------------------*------------------------------------------*
+| SMNT | -- | SmntCommandHandler |
+*------------------------*------------------------*------------------------------------------*
+| STAT | STATUS | StatCommandHandler |
+*------------------------*------------------------*------------------------------------------*
+| STOR | PUT / SEND | StorCommandHandler |
+*------------------------*------------------------*------------------------------------------*
+| STOU | -- | StouCommandHandler |
+*------------------------*------------------------*------------------------------------------*
+| STRU | -- | StruCommandHandler |
+*------------------------*------------------------*------------------------------------------*
+| SYST | -- | SystCommandHandler |
+*------------------------*------------------------*------------------------------------------*
+| TYPE | ASCII / BINARY / TYPE | TypeCommandHandler |
+*------------------------*------------------------*------------------------------------------*
+| USER | USER | UserCommandHandler |
+*------------------------*------------------------*------------------------------------------*
+
+ (1) This is not a <real> FTP Server command, but rather encapsulates the sending of the
+ 220 reply code after the initial connection to the server.
+
+ (2) An alternative to the default <CommandHandler> implementation. See its class Javadoc.
+
diff --git a/tags/1.2.3/src/site/apt/stubftpserver-features.apt b/tags/1.2.3/src/site/apt/stubftpserver-features.apt
new file mode 100644
index 0000000..cf43ef2
--- /dev/null
+++ b/tags/1.2.3/src/site/apt/stubftpserver-features.apt
@@ -0,0 +1,32 @@
+ --------------------------------------------------
+ StubFtpServer Features and Limitations
+ --------------------------------------------------
+
+StubFtpServer Features
+
+ * Standalone dummy FTP server. Run either within the same JVM as test code or in a different JVM.
+
+ * Implements common FTP server commands. See {{{stubftpserver-commandhandlers.html}FTP Commands and CommandHandlers}}.
+
+ * Supports active and passive mode data transfers.
+
+ * Works out of the box with reasonable defaults: success reply codes and empty data.
+
+ * Easy to configure command handlers for individual FTP server commands to return success/failure reply codes and custom data.
+
+ * Can verify expected FTP server command invocations.
+
+ * Easy to implement command handlers for other commands or replace existing command handlers.
+
+ * Fully supports configuration within the <<Spring Framework>>.
+
+ * Can be used to test FTP client code written in any language
+
+
+StubFtpServer Limitations
+
+ * Using <<StubFtpServer>> for testing and simulation of non-default scenarios requires
+ some understanding of the FTP Specification and a configuration of the low-level
+ FTP Server commands.
+
+ \ No newline at end of file
diff --git a/tags/1.2.3/src/site/apt/stubftpserver-getting-started.apt b/tags/1.2.3/src/site/apt/stubftpserver-getting-started.apt
new file mode 100644
index 0000000..19de649
--- /dev/null
+++ b/tags/1.2.3/src/site/apt/stubftpserver-getting-started.apt
@@ -0,0 +1,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.
diff --git a/tags/1.2.3/src/site/fml/faq.fml b/tags/1.2.3/src/site/fml/faq.fml
new file mode 100644
index 0000000..c4d1495
--- /dev/null
+++ b/tags/1.2.3/src/site/fml/faq.fml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<faqs title="Frequently Asked Questions" toplink="false">
+
+ <part id="general">
+ <title>General</title>
+
+ <faq id="whats-foo">
+ <question>
+ What is Foo?
+ </question>
+ <answer>
+ <p>some markup goes here</p>
+
+ <source>some source code</source>
+
+ <p>some markup goes here</p>
+ </answer>
+ </faq>
+
+ <!--
+ <faq id="whats-bar">
+ <question>
+ What is Bar?
+ </question>
+ <answer>
+ <p>some markup goes here</p>
+ </answer>
+ </faq>
+ -->
+
+ </part>
+
+ <!--
+ <part id="install">
+
+ <title>Installation</title>
+
+ <faq id="how-install">
+ <question>
+ How do I install Foo?
+ </question>
+ <answer>
+ <p>some markup goes here</p>
+ </answer>
+ </faq>
+
+ </part>
+ -->
+
+</faqs>
diff --git a/tags/1.2.3/src/site/resources/css/site.css b/tags/1.2.3/src/site/resources/css/site.css
new file mode 100644
index 0000000..4f24aa3
--- /dev/null
+++ b/tags/1.2.3/src/site/resources/css/site.css
@@ -0,0 +1,4 @@
+tt {
+ font-size: 110%;
+ font-weight: bolder;
+} \ No newline at end of file
diff --git a/tags/1.2.3/src/site/site.xml b/tags/1.2.3/src/site/site.xml
new file mode 100644
index 0000000..6f38a33
--- /dev/null
+++ b/tags/1.2.3/src/site/site.xml
@@ -0,0 +1,43 @@
+<project name="MockFtpServer">
+
+ <bannerLeft>
+ <name>MockFtpServer</name>
+ <href>/</href>
+ </bannerLeft>
+
+ <publishDate format="dd MMM yyyy" />
+
+ <poweredBy>
+ <logo
+ name="Hosted on SourceForge.net"
+ href="http://sourceforge.net"
+ img="http://sflogo.sourceforge.net/sflogo.php?group_id=208647&amp;type=2"/>
+ <logo
+ name="Build with Maven 2"
+ href="http://maven.apache.org"
+ img="images/logos/maven-feather.png"/>
+ </poweredBy>
+
+ <body>
+ <links>
+ </links>
+ <head>
+ <meta name="faq" content="mockftpserver"/>
+ </head>
+ <menu name="General">
+ <item name="Home" href="/index.html"/>
+ <!-- <item name="FAQs" href="/faq.html"/> -->
+ <item name="Javadocs" href="/apidocs/index.html"/>
+ <item name="Downloads" href="http://sourceforge.net/project/showfiles.php?group_id=208647"/>
+ <item name="SourceForge Project Page" href="http://sourceforge.net/projects/mockftpserver"/>
+ </menu>
+
+ <menu name="StubFtpServer">
+ <item name="Features and Limitations" href="/stubftpserver-features.html"/>
+ <item name="Getting Started" href="/stubftpserver-getting-started.html"/>
+ <item name="CommandHandlers" href="/stubftpserver-commandhandlers.html"/>
+ </menu>
+
+ ${reports}
+ </body>
+</project> \ No newline at end of file
diff --git a/tags/1.2.3/src/test/java/org/mockftpserver/core/command/InvocationRecordTest.java b/tags/1.2.3/src/test/java/org/mockftpserver/core/command/InvocationRecordTest.java
new file mode 100644
index 0000000..70d4f35
--- /dev/null
+++ b/tags/1.2.3/src/test/java/org/mockftpserver/core/command/InvocationRecordTest.java
@@ -0,0 +1,228 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.core.command;
+
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Set;
+
+
+import org.apache.log4j.Logger;
+import org.mockftpserver.core.command.Command;
+import org.mockftpserver.core.command.InvocationRecord;
+import org.mockftpserver.core.util.AssertFailedException;
+import org.mockftpserver.test.AbstractTest;
+
+/**
+ * Tests for InvocationRecord
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public final class InvocationRecordTest extends AbstractTest {
+
+ private static final Logger LOG = Logger.getLogger(InvocationRecordTest.class);
+ private static final Command COMMAND = new Command("command", EMPTY);
+ private static final String KEY1 = "key1";
+ private static final String KEY2 = "key2";
+ private static final String STRING = "abc123";
+ private static final Integer INT = new Integer(77);
+
+ private InvocationRecord invocationRecord;
+
+ /**
+ * Test the Constructor
+ */
+ public void testConstructor() {
+ final Command COMMAND = new Command("ABC", EMPTY);
+ long beforeTime = System.currentTimeMillis();
+ InvocationRecord commandInvocation = new InvocationRecord(COMMAND, DEFAULT_HOST);
+ long afterTime = System.currentTimeMillis();
+ LOG.info(commandInvocation);
+ assertEquals("Command", COMMAND, commandInvocation.getCommand());
+ assertTrue("time", commandInvocation.getTime().getTime() >= beforeTime
+ && commandInvocation.getTime().getTime() <= afterTime);
+ assertEquals("host", DEFAULT_HOST, commandInvocation.getClientHost());
+ }
+
+ /**
+ * Test the set() method, passing in a null key
+ */
+ public void testSet_NullKey() {
+ try {
+ invocationRecord.set(null, STRING);
+ fail("Expected AssertFailedException");
+ }
+ catch (AssertFailedException expected) {
+ LOG.info("Expected: " + expected);
+ }
+ }
+
+ /**
+ * Test the set() method, passing in a null value
+ */
+ public void testSet_NullValue() {
+ invocationRecord.set(KEY1, null);
+ assertNull(KEY1, invocationRecord.getObject(KEY1));
+ }
+
+ /**
+ * Test the containsKey() method
+ */
+ public void testContainsKey() {
+ invocationRecord.set(KEY1, STRING);
+ assertTrue(KEY1, invocationRecord.containsKey(KEY1));
+ assertFalse(KEY2, invocationRecord.containsKey(KEY2));
+ }
+
+ /**
+ * Test the containsKey() method, passing in a null key
+ */
+ public void testContainsKey_Null() {
+ try {
+ invocationRecord.containsKey(null);
+ fail("Expected AssertFailedException");
+ }
+ catch (AssertFailedException expected) {
+ LOG.info("Expected: " + expected);
+ }
+ }
+
+ /**
+ * Test the getString() method
+ */
+ public void testGetString() {
+ assertNull("undefined", invocationRecord.getString("UNDEFINED"));
+ invocationRecord.set(KEY1, STRING);
+ assertEquals(KEY1, STRING, invocationRecord.getString(KEY1));
+ }
+
+ /**
+ * Test the getString() method, passing in a null key
+ */
+ public void testGetString_Null() {
+ try {
+ invocationRecord.getString(null);
+ fail("Expected AssertFailedException");
+ }
+ catch (AssertFailedException expected) {
+ LOG.info("Expected: " + expected);
+ }
+ }
+
+ /**
+ * Test the getString() method, when the value for the key is not a String
+ */
+ public void testGetString_NotAString() {
+
+ invocationRecord.set(KEY1, INT);
+ try {
+ invocationRecord.getString(KEY1);
+ fail("Expected ClassCastException");
+ }
+ catch (ClassCastException expected) {
+ LOG.info("Expected: " + expected);
+ }
+ }
+
+ /**
+ * Test the getObject() method
+ */
+ public void testGetObject() {
+ assertNull("undefined", invocationRecord.getObject("UNDEFINED"));
+ invocationRecord.set(KEY1, STRING);
+ assertEquals(KEY1, STRING, invocationRecord.getObject(KEY1));
+ }
+
+ /**
+ * Test the keySet() method
+ */
+ public void testKeySet() {
+ Set set = new HashSet();
+ assertEquals("empty", set, invocationRecord.keySet());
+ invocationRecord.set(KEY1, STRING);
+ invocationRecord.set(KEY2, STRING);
+ set.add(KEY1);
+ set.add(KEY2);
+ assertEquals("2", set, invocationRecord.keySet());
+ }
+
+ /**
+ * Test that the keySet() return value does not allow breaking immutability
+ */
+ public void testKeySet_Immutability() {
+ Set keySet = invocationRecord.keySet();
+ try {
+ keySet.add("abc");
+ fail("Expected UnsupportedOperationException");
+ }
+ catch (UnsupportedOperationException expected) {
+ LOG.info("Expected: " + expected);
+ }
+ }
+
+ /**
+ * Test the getObject() method, passing in a null key
+ */
+ public void testGetObject_Null() {
+ try {
+ invocationRecord.getObject(null);
+ fail("Expected AssertFailedException");
+ }
+ catch (AssertFailedException expected) {
+ LOG.info("Expected: " + expected);
+ }
+ }
+
+ /**
+ * Test the lock() method
+ */
+ public void testLock() {
+ assertFalse("locked - before", invocationRecord.isLocked());
+ invocationRecord.set(KEY1, STRING);
+ invocationRecord.lock();
+ assertTrue("locked - after", invocationRecord.isLocked());
+ try {
+ invocationRecord.set(KEY2, "abc");
+ fail("Expected AssertFailedException");
+ }
+ catch (AssertFailedException expected) {
+ LOG.info("Expected: " + expected);
+ }
+ }
+
+ /**
+ * Test that the getTime() return value does not break immutability
+ */
+ public void testGetTime_Immutability() {
+
+ Date timestamp = invocationRecord.getTime();
+ long timeInMillis = timestamp.getTime();
+ timestamp.setTime(12345L);
+ assertEquals("time", timeInMillis, invocationRecord.getTime().getTime());
+ }
+
+ /**
+ * Perform initialization before each test
+ *
+ * @see org.mockftpserver.test.AbstractTest#setUp()
+ */
+ protected void setUp() throws Exception {
+ super.setUp();
+ invocationRecord = new InvocationRecord(COMMAND, DEFAULT_HOST);
+ }
+}
diff --git a/tags/1.2.3/src/test/java/org/mockftpserver/core/command/ReplyTextBundleUtilTest.java b/tags/1.2.3/src/test/java/org/mockftpserver/core/command/ReplyTextBundleUtilTest.java
new file mode 100644
index 0000000..0bf9252
--- /dev/null
+++ b/tags/1.2.3/src/test/java/org/mockftpserver/core/command/ReplyTextBundleUtilTest.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.core.command;
+
+import java.util.ListResourceBundle;
+import java.util.ResourceBundle;
+
+import org.apache.log4j.Logger;
+import org.mockftpserver.core.util.AssertFailedException;
+import org.mockftpserver.test.AbstractTest;
+
+/**
+ * Tests for the ReplyTextBundleUtil class.
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public final class ReplyTextBundleUtilTest extends AbstractTest {
+
+ private static final Logger LOG = Logger.getLogger(ReplyTextBundleUtilTest.class);
+
+ private ResourceBundle resourceBundle1;
+ private ResourceBundle resourceBundle2;
+
+ /**
+ * Test the setReplyTextBundleIfAppropriate() method, when the CommandHandler implements
+ * the ResourceBundleAware interface, and the replyTextBundle has not yet been set.
+ */
+ public void testSetReplyTextBundleIfAppropriate_ReplyTextBundleAware_NotSetYet() {
+ AbstractCommandHandler commandHandler = new StaticReplyCommandHandler();
+ ReplyTextBundleUtil.setReplyTextBundleIfAppropriate(commandHandler, resourceBundle1);
+ assertSame(resourceBundle1, commandHandler.getReplyTextBundle());
+ }
+
+ /**
+ * Test the setReplyTextBundleIfAppropriate() method, when the CommandHandler implements
+ * the ResourceBundleAware interface, and the replyTextBundle has already been set.
+ */
+ public void testSetReplyTextBundleIfAppropriate_ReplyTextBundleAware_AlreadySet() {
+ AbstractCommandHandler commandHandler = new StaticReplyCommandHandler();
+ commandHandler.setReplyTextBundle(resourceBundle2);
+ ReplyTextBundleUtil.setReplyTextBundleIfAppropriate(commandHandler, resourceBundle1);
+ assertSame(resourceBundle2, commandHandler.getReplyTextBundle());
+ }
+
+ /**
+ * Test the setReplyTextBundleIfAppropriate() method, when the CommandHandler does not
+ * implement the ResourceBundleAware interface.
+ */
+ public void testSetReplyTextBundleIfAppropriate_NotReplyTextBundleAware() {
+ CommandHandler commandHandler = (CommandHandler) createMock(CommandHandler.class);
+ replay(commandHandler);
+ ReplyTextBundleUtil.setReplyTextBundleIfAppropriate(commandHandler, resourceBundle1);
+ verify(commandHandler); // expect no method calls
+ }
+
+ /**
+ * Test the setReplyTextBundleIfAppropriate() method, when the CommandHandler is null.
+ */
+ public void testSetReplyTextBundleIfAppropriate_NullCommandHandler() {
+ try {
+ ReplyTextBundleUtil.setReplyTextBundleIfAppropriate(null, resourceBundle1);
+ fail("Expected AssertFailedException");
+ }
+ catch (AssertFailedException expected) {
+ LOG.info("Expected: " + expected);
+ }
+ }
+
+ /**
+ * @see org.mockftpserver.test.AbstractTest#setUp()
+ */
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ resourceBundle1 = new ListResourceBundle() {
+ protected Object[][] getContents() {
+ return null;
+ }
+ };
+
+ resourceBundle2 = new ListResourceBundle() {
+ protected Object[][] getContents() {
+ return new Object[][] { { "a", "b" } };
+ }
+ };
+ }
+
+}
diff --git a/tags/1.2.3/src/test/java/org/mockftpserver/core/command/SimpleCompositeCommandHandlerTest.java b/tags/1.2.3/src/test/java/org/mockftpserver/core/command/SimpleCompositeCommandHandlerTest.java
new file mode 100644
index 0000000..9d0a3ae
--- /dev/null
+++ b/tags/1.2.3/src/test/java/org/mockftpserver/core/command/SimpleCompositeCommandHandlerTest.java
@@ -0,0 +1,272 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.core.command;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.ListResourceBundle;
+import java.util.ResourceBundle;
+
+
+import org.apache.log4j.Logger;
+import org.mockftpserver.core.command.Command;
+import org.mockftpserver.core.command.CommandHandler;
+import org.mockftpserver.core.command.SimpleCompositeCommandHandler;
+import org.mockftpserver.core.session.Session;
+import org.mockftpserver.core.util.AssertFailedException;
+import org.mockftpserver.test.AbstractTest;
+
+/**
+ * Tests for SimpleCompositeCommandHandler
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public class SimpleCompositeCommandHandlerTest extends AbstractTest {
+
+ private static final Logger LOG = Logger.getLogger(SimpleCompositeCommandHandlerTest.class);
+
+ private SimpleCompositeCommandHandler simpleCompositeCommandHandler;
+ private Session session;
+ private Command command;
+ private CommandHandler commandHandler1;
+ private CommandHandler commandHandler2;
+ private CommandHandler commandHandler3;
+
+ /**
+ * Test the handleCommand() method
+ */
+ public void testHandleCommand_OneHandler_OneInvocation() throws Exception {
+ simpleCompositeCommandHandler.addCommandHandler(commandHandler1);
+
+ commandHandler1.handleCommand(command, session);
+ replay(commandHandler1);
+
+ simpleCompositeCommandHandler.handleCommand(command, session);
+ verify(commandHandler1);
+ }
+
+ /**
+ * Test the handleCommand() method, with two CommandHandler defined, but with multiple invocation
+ */
+ public void testHandleCommand_TwoHandlers() throws Exception {
+ simpleCompositeCommandHandler.addCommandHandler(commandHandler1);
+ simpleCompositeCommandHandler.addCommandHandler(commandHandler2);
+
+ commandHandler1.handleCommand(command, session);
+ commandHandler2.handleCommand(command, session);
+ replayAll();
+
+ simpleCompositeCommandHandler.handleCommand(command, session);
+ simpleCompositeCommandHandler.handleCommand(command, session);
+ verifyAll();
+ }
+
+ /**
+ * Test the handleCommand() method, with three CommandHandler defined, and multiple invocation
+ */
+ public void testHandleCommand_ThreeHandlers() throws Exception {
+
+ List list = new ArrayList();
+ list.add(commandHandler1);
+ list.add(commandHandler2);
+ list.add(commandHandler3);
+ simpleCompositeCommandHandler.setCommandHandlers(list);
+
+ commandHandler1.handleCommand(command, session);
+ commandHandler2.handleCommand(command, session);
+ commandHandler3.handleCommand(command, session);
+ replayAll();
+
+ simpleCompositeCommandHandler.handleCommand(command, session);
+ simpleCompositeCommandHandler.handleCommand(command, session);
+ simpleCompositeCommandHandler.handleCommand(command, session);
+ verifyAll();
+ }
+
+ /**
+ * Test the handleCommand() method, with a single CommandHandler defined, but too many invocations
+ */
+ public void testHandleCommand_OneHandler_TooManyInvocations() throws Exception {
+ simpleCompositeCommandHandler.addCommandHandler(commandHandler1);
+
+ commandHandler1.handleCommand(command, session);
+ replay(commandHandler1);
+
+ simpleCompositeCommandHandler.handleCommand(command, session);
+
+ // Second invocation throws an exception
+ try {
+ simpleCompositeCommandHandler.handleCommand(command, session);
+ fail("Expected AssertFailedException");
+ }
+ catch (AssertFailedException expected) {
+ LOG.info("Expected: " + expected);
+ }
+ }
+
+ /**
+ * Test the handleCommand_NoHandlersDefined() method
+ */
+ public void testHandleCommand_NoHandlersDefined() throws Exception {
+ try {
+ simpleCompositeCommandHandler.handleCommand(command, session);
+ fail("Expected AssertFailedException");
+ }
+ catch(AssertFailedException expected) {
+ LOG.info("Expected: " + expected);
+ }
+ }
+
+ /**
+ * Test the handleCommand(Command,Session) method, passing in a null Command
+ */
+ public void testHandleCommand_NullCommand() throws Exception {
+ try {
+ simpleCompositeCommandHandler.handleCommand(null, session);
+ fail("Expected AssertFailedException");
+ }
+ catch (AssertFailedException expected) {
+ LOG.info("Expected: " + expected);
+ }
+ }
+
+ /**
+ * Test the handleCommand(Command,Session) method, passing in a null Session
+ */
+ public void testHandleCommand_NullSession() throws Exception {
+ try {
+ simpleCompositeCommandHandler.handleCommand(command, null);
+ fail("Expected AssertFailedException");
+ }
+ catch (AssertFailedException expected) {
+ LOG.info("Expected: " + expected);
+ }
+ }
+
+ /**
+ * Test the addCommandHandler(CommandHandler) method, passing in a null CommandHandler
+ */
+ public void testAddCommandHandler_NullCommandHandler() throws Exception {
+ try {
+ simpleCompositeCommandHandler.addCommandHandler(null);
+ fail("Expected AssertFailedException");
+ }
+ catch (AssertFailedException expected) {
+ LOG.info("Expected: " + expected);
+ }
+ }
+
+ /**
+ * Test the setCommandHandlers(List) method, passing in a null
+ */
+ public void testSetCommandHandlers_Null() throws Exception {
+ try {
+ simpleCompositeCommandHandler.setCommandHandlers(null);
+ fail("Expected AssertFailedException");
+ }
+ catch (AssertFailedException expected) {
+ LOG.info("Expected: " + expected);
+ }
+ }
+
+ /**
+ * Test the getCommandHandler(int) method, passing in an index for which no CommandHandler is defined
+ */
+ public void testGetCommandHandler_UndefinedIndex() throws Exception {
+ simpleCompositeCommandHandler.addCommandHandler(commandHandler1);
+ try {
+ simpleCompositeCommandHandler.getCommandHandler(1);
+ fail("Expected AssertFailedException");
+ }
+ catch (AssertFailedException expected) {
+ LOG.info("Expected: " + expected);
+ }
+ }
+
+ /**
+ * Test the getCommandHandler(int) method
+ */
+ public void testGetCommandHandler() throws Exception {
+ simpleCompositeCommandHandler.addCommandHandler(commandHandler1);
+ simpleCompositeCommandHandler.addCommandHandler(commandHandler2);
+ assertSame("index 0", commandHandler1, simpleCompositeCommandHandler.getCommandHandler(0));
+ assertSame("index 1", commandHandler2, simpleCompositeCommandHandler.getCommandHandler(1));
+ }
+
+ /**
+ * Test the getCommandHandler(int) method, passing in a negative index
+ */
+ public void testGetCommandHandler_NegativeIndex() throws Exception {
+ simpleCompositeCommandHandler.addCommandHandler(commandHandler1);
+ try {
+ simpleCompositeCommandHandler.getCommandHandler(-1);
+ fail("Expected AssertFailedException");
+ }
+ catch (AssertFailedException expected) {
+ LOG.info("Expected: " + expected);
+ }
+ }
+
+ /**
+ * Test the getReplyTextBundle() method
+ */
+ public void testGetReplyTextBundle() {
+ assertNull(simpleCompositeCommandHandler.getReplyTextBundle());
+ }
+
+ /**
+ * Test the setReplyTextBundle() method
+ */
+ public void testSetReplyTextBundle() {
+
+ AbstractCommandHandler replyTextBundleAwareCommandHandler1 = new StaticReplyCommandHandler();
+ AbstractCommandHandler replyTextBundleAwareCommandHandler2 = new StaticReplyCommandHandler();
+ simpleCompositeCommandHandler.addCommandHandler(replyTextBundleAwareCommandHandler1);
+ simpleCompositeCommandHandler.addCommandHandler(commandHandler1);
+ simpleCompositeCommandHandler.addCommandHandler(replyTextBundleAwareCommandHandler2);
+
+ ResourceBundle resourceBundle = new ListResourceBundle() {
+ protected Object[][] getContents() {
+ return null;
+ }
+ };
+
+ simpleCompositeCommandHandler.setReplyTextBundle(resourceBundle);
+ assertSame("1", resourceBundle, replyTextBundleAwareCommandHandler1.getReplyTextBundle());
+ assertSame("2", resourceBundle, replyTextBundleAwareCommandHandler1.getReplyTextBundle());
+ }
+
+ //-------------------------------------------------------------------------
+ // Test setup
+ //-------------------------------------------------------------------------
+
+ /**
+ * Perform initialization before each test
+ * @see org.mockftpserver.test.AbstractTest#setUp()
+ */
+ protected void setUp() throws Exception {
+ super.setUp();
+ simpleCompositeCommandHandler = new SimpleCompositeCommandHandler();
+ session = (Session) createMock(Session.class);
+ command = new Command("cmd", EMPTY);
+ commandHandler1 = (CommandHandler) createMock(CommandHandler.class);
+ commandHandler2 = (CommandHandler) createMock(CommandHandler.class);
+ commandHandler3 = (CommandHandler) createMock(CommandHandler.class);
+ }
+
+}
diff --git a/tags/1.2.3/src/test/java/org/mockftpserver/core/command/StaticReplyCommandHandlerTest.java b/tags/1.2.3/src/test/java/org/mockftpserver/core/command/StaticReplyCommandHandlerTest.java
new file mode 100644
index 0000000..dd072b7
--- /dev/null
+++ b/tags/1.2.3/src/test/java/org/mockftpserver/core/command/StaticReplyCommandHandlerTest.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.core.command;
+
+import org.apache.log4j.Logger;
+import org.mockftpserver.core.command.Command;
+import org.mockftpserver.core.command.StaticReplyCommandHandler;
+import org.mockftpserver.core.util.AssertFailedException;
+import org.mockftpserver.stub.command.AbstractCommandHandlerTest;
+
+/**
+ * Tests for the StaticReplyCommandHandler class
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public final class StaticReplyCommandHandlerTest extends AbstractCommandHandlerTest {
+
+ private static final Logger LOG = Logger.getLogger(StaticReplyCommandHandlerTest.class);
+ private static final int REPLY_CODE = 999;
+ private static final String REPLY_TEXT = "some text 123";
+ private static final Command COMMAND = new Command("ANY", EMPTY);
+
+ private StaticReplyCommandHandler commandHandler;
+
+ /**
+ * Test the constructor that takes a replyCode, passing in a null
+ */
+ public void testConstructor_String_InvalidReplyCode() {
+ try {
+ new StaticReplyCommandHandler(-1);
+ fail("Expected AssertFailedException");
+ }
+ catch (AssertFailedException expected) {
+ LOG.info("Expected: " + expected);
+ }
+ }
+
+ /**
+ * Test the constructor that takes a replyCode and replyText, passing in a null replyCode
+ */
+ public void testConstructor_StringString_InvalidReplyCode() {
+ try {
+ new StaticReplyCommandHandler(-99, "text");
+ fail("Expected AssertFailedException");
+ }
+ catch (AssertFailedException expected) {
+ LOG.info("Expected: " + expected);
+ }
+ }
+
+ /**
+ * Test the setReplyCode() method, passing in a null
+ */
+ public void testSetReplyCode_Invalid() {
+ try {
+ commandHandler.setReplyCode(-1);
+ fail("Expected AssertFailedException");
+ }
+ catch (AssertFailedException expected) {
+ LOG.info("Expected: " + expected);
+ }
+ }
+
+ /**
+ * Test the handleCommand() method when the replyText attribute has not been set.
+ * So, use whatever replyText has been configured in the replyCodeMapping
+ * @throws Exception
+ */
+ public void testHandleCommand_ReplyTextNotSet() throws Exception {
+ commandHandler.setReplyCode(250);
+
+ session.sendReply(250, replyTextFor(250));
+ replay(session);
+
+ commandHandler.handleCommand(COMMAND, session);
+ verify(session);
+
+ verifyNumberOfInvocations(commandHandler, 1);
+ verifyNoDataElements(commandHandler.getInvocation(0));
+ }
+
+ /**
+ * Test the handleCommand() method, when the replyCode and replyText are both set
+ * @throws Exception
+ */
+ public void testHandleCommand_SetReplyText() throws Exception {
+ commandHandler.setReplyCode(REPLY_CODE);
+ commandHandler.setReplyText(REPLY_TEXT);
+
+ session.sendReply(REPLY_CODE, REPLY_TEXT);
+ replay(session);
+
+ commandHandler.handleCommand(COMMAND, session);
+ verify(session);
+
+ verifyNumberOfInvocations(commandHandler, 1);
+ verifyNoDataElements(commandHandler.getInvocation(0));
+ }
+
+ /**
+ * Test the handleCommand() method when the replyCode attribute has not been set
+ * @throws Exception
+ */
+ public void testHandleCommand_ReplyCodeNotSet() throws Exception {
+
+ try {
+ commandHandler.handleCommand(COMMAND, session);
+ fail("Expected AssertFailedException");
+ }
+ catch (AssertFailedException expected) {
+ LOG.info("Expected: " + expected);
+ }
+
+ verifyNumberOfInvocations(commandHandler, 1);
+ verifyNoDataElements(commandHandler.getInvocation(0));
+ }
+
+ /**
+ * @see org.mockftpserver.stub.command.AbstractCommandHandlerTest#setUp()
+ */
+ protected void setUp() throws Exception {
+ super.setUp();
+ commandHandler = new StaticReplyCommandHandler();
+ commandHandler.setReplyTextBundle(replyTextBundle);
+ }
+
+}
diff --git a/tags/1.2.3/src/test/java/org/mockftpserver/core/command/_AbstractCommandHandlerTest.java b/tags/1.2.3/src/test/java/org/mockftpserver/core/command/_AbstractCommandHandlerTest.java
new file mode 100644
index 0000000..2cc9a5e
--- /dev/null
+++ b/tags/1.2.3/src/test/java/org/mockftpserver/core/command/_AbstractCommandHandlerTest.java
@@ -0,0 +1,269 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.core.command;
+
+import java.util.ListResourceBundle;
+import java.util.ResourceBundle;
+
+import org.apache.log4j.Logger;
+import org.easymock.MockControl;
+import org.mockftpserver.core.session.Session;
+import org.mockftpserver.core.util.AssertFailedException;
+import org.mockftpserver.stub.command.AbstractStubCommandHandler;
+import org.mockftpserver.test.AbstractTest;
+
+/**
+ * Tests for the AbstractCommandHandler class. The class name is prefixed with an underscore
+ * so that it is not filtered out by Maven's Surefire test plugin.
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public final class _AbstractCommandHandlerTest extends AbstractTest {
+
+ private static final Logger LOG = Logger.getLogger(_AbstractCommandHandlerTest.class);
+ private static final String COMMAND_NAME = "abc";
+ private static final Object ARG = "123";
+ private static final Object[] ARGS = { ARG };
+ private static final Command COMMAND = new Command(COMMAND_NAME, EMPTY);
+ private static final Command COMMAND_WITH_ARGS = new Command(COMMAND_NAME, EMPTY);
+ private static final int REPLY_CODE1 = 777;
+ private static final int REPLY_CODE2 = 888;
+ private static final int REPLY_CODE3 = 999;
+ private static final String REPLY_TEXT1 = "reply1 ... abcdef";
+ private static final String REPLY_TEXT2 = "abc {0} def";
+ private static final String REPLY_TEXT2_FORMATTED = "abc 123 def";
+ private static final String OVERRIDE_REPLY_TEXT = "overridden reply ... abcdef";
+ private static final String MESSAGE_KEY = "key.123";
+ private static final String MESSAGE_TEXT = "message.123";
+
+ private AbstractCommandHandler commandHandler;
+ private Session session;
+ private ResourceBundle replyTextBundle;
+
+ /**
+ * Test the handleCommand(Command,Session) method
+ */
+ public void testHandleCommand() throws Exception {
+ assertEquals("before", 0, commandHandler.numberOfInvocations());
+ commandHandler.handleCommand(COMMAND, session);
+ assertEquals("after", 1, commandHandler.numberOfInvocations());
+ assertTrue("locked", commandHandler.getInvocation(0).isLocked());
+ }
+
+ /**
+ * Test the handleCommand(Command,Session) method, passing in a null Command
+ */
+ public void testHandleCommand_NullCommand() throws Exception {
+ try {
+ commandHandler.handleCommand(null, session);
+ fail("Expected AssertFailedException");
+ }
+ catch (AssertFailedException expected) {
+ LOG.info("Expected: " + expected);
+ }
+ }
+
+ /**
+ * Test the handleCommand(Command,Session) method, passing in a null Session
+ */
+ public void testHandleCommand_NullSession() throws Exception {
+ try {
+ commandHandler.handleCommand(COMMAND, null);
+ fail("Expected AssertFailedException");
+ }
+ catch (AssertFailedException expected) {
+ LOG.info("Expected: " + expected);
+ }
+ }
+
+ /**
+ * Test the numberOfInvocations(), addInvocationRecord() and clearInvocationRecord() methods
+ */
+ public void testInvocationHistory() throws Exception {
+ control(session).expectAndDefaultReturn(session.getClientHost(), DEFAULT_HOST);
+ replay(session);
+
+ assertEquals("none", 0, commandHandler.numberOfInvocations());
+ commandHandler.handleCommand(COMMAND, session);
+ assertEquals("1", 1, commandHandler.numberOfInvocations());
+ commandHandler.handleCommand(COMMAND, session);
+ assertEquals("2", 2, commandHandler.numberOfInvocations());
+ commandHandler.clearInvocations();
+ assertEquals("cleared", 0, commandHandler.numberOfInvocations());
+ }
+
+ /**
+ * Test the getInvocation() method
+ * @throws Exception
+ */
+ public void testGetInvocation() throws Exception {
+ control(session).expectAndDefaultReturn(session.getClientHost(), DEFAULT_HOST);
+ replay(session);
+
+ commandHandler.handleCommand(COMMAND, session);
+ commandHandler.handleCommand(COMMAND_WITH_ARGS, session);
+ assertSame("1", COMMAND, commandHandler.getInvocation(0).getCommand());
+ assertSame("2", COMMAND_WITH_ARGS, commandHandler.getInvocation(1).getCommand());
+ }
+
+ /**
+ * Test the getInvocation() method, passing in an invalid index
+ */
+ public void testGetInvocation_IndexOutOfBounds() throws Exception {
+ commandHandler.handleCommand(COMMAND, session);
+ try {
+ commandHandler.getInvocation(2);
+ fail("Expected IndexOutOfBoundsException");
+ }
+ catch (IndexOutOfBoundsException expected) {
+ LOG.info("Expected: " + expected);
+ }
+ }
+
+ /**
+ * Test the quotes utility method
+ */
+ public void testQuotes() {
+ assertEquals("abc", "\"abc\"", AbstractStubCommandHandler.quotes("abc"));
+ assertEquals("<empty>", "\"\"", AbstractStubCommandHandler.quotes(""));
+ }
+
+ /**
+ * Test the quotes utility method, passing in a null
+ */
+ public void testQuotes_Null() {
+ try {
+ AbstractStubCommandHandler.quotes(null);
+ fail("Expected AssertFailedException");
+ }
+ catch (AssertFailedException expected) {
+ LOG.info("Expected: " + expected);
+ }
+ }
+
+ /**
+ * Test the assertValidReplyCode() method
+ */
+ public void testAssertValidReplyCode() {
+ // These are valid, so expect no exceptions
+ commandHandler.assertValidReplyCode(1);
+ commandHandler.assertValidReplyCode(100);
+
+ // These are invalid
+ testAssertValidReplyCodeWithInvalid(0);
+ testAssertValidReplyCodeWithInvalid(-1);
+ }
+
+ /**
+ * Test the assertValidReplyCode() method , passing in an invalid replyCode value
+ */
+ private void testAssertValidReplyCodeWithInvalid(int invalidReplyCode) {
+ try {
+ commandHandler.assertValidReplyCode(invalidReplyCode);
+ fail("Expected AssertFailedException");
+ }
+ catch (AssertFailedException expected) {
+ LOG.info("Expected: " + expected);
+ }
+ }
+
+ /**
+ * Test the sendReply() method, when no message arguments are specified
+ */
+ public void testSendReply() {
+ session.sendReply(REPLY_CODE1, REPLY_TEXT1);
+ session.sendReply(REPLY_CODE1, MESSAGE_TEXT);
+ session.sendReply(REPLY_CODE1, OVERRIDE_REPLY_TEXT);
+ session.sendReply(REPLY_CODE3, null);
+ replay(session);
+
+ commandHandler.sendReply(session, REPLY_CODE1, null, null, null);
+ commandHandler.sendReply(session, REPLY_CODE1, MESSAGE_KEY, null, null);
+ commandHandler.sendReply(session, REPLY_CODE1, MESSAGE_KEY, OVERRIDE_REPLY_TEXT, null);
+ commandHandler.sendReply(session, REPLY_CODE3, null, null, null);
+
+ verify(session);
+ }
+
+ /**
+ * Test the sendReply() method, passing in message arguments
+ */
+ public void testSendReply_WithMessageArguments() {
+ session.sendReply(REPLY_CODE1, REPLY_TEXT2_FORMATTED);
+ replay(session);
+
+ commandHandler.sendReply(session, REPLY_CODE1, null, REPLY_TEXT2, ARGS);
+
+ verify(session);
+ }
+
+ /**
+ * Test the sendReply() method, passing in a null Session
+ */
+ public void testSendReply_NullSession() {
+ try {
+ commandHandler.sendReply(null, REPLY_CODE1, REPLY_TEXT1, null, null);
+ fail("Expected AssertFailedException");
+ }
+ catch (AssertFailedException expected) {
+ LOG.info("Expected: " + expected);
+ }
+ }
+
+ /**
+ * Test the sendReply() method, passing in an invalid replyCode
+ */
+ public void testSendReply_InvalidReplyCode() {
+ try {
+ commandHandler.sendReply(session, 0, REPLY_TEXT1, null, null);
+ fail("Expected AssertFailedException");
+ }
+ catch (AssertFailedException expected) {
+ LOG.info("Expected: " + expected);
+ }
+ }
+
+ //-------------------------------------------------------------------------
+ // Test setup
+ //-------------------------------------------------------------------------
+
+ /**
+ * Perform initialization before each test
+ * @see org.mockftpserver.test.AbstractTest#setUp()
+ */
+ protected void setUp() throws Exception {
+ super.setUp();
+ session = (Session) createMock(Session.class);
+ control(session).setDefaultMatcher(MockControl.ARRAY_MATCHER);
+ commandHandler = new AbstractCommandHandler() {
+ public void handleCommand(Command command, Session session, InvocationRecord invocationRecord) throws Exception {
+ }
+ };
+ replyTextBundle = new ListResourceBundle() {
+ protected Object[][] getContents() {
+ return new Object[][] {
+ { Integer.toString(REPLY_CODE1), REPLY_TEXT1 },
+ { Integer.toString(REPLY_CODE2), REPLY_TEXT2 },
+ { MESSAGE_KEY, MESSAGE_TEXT }
+ };
+ }
+ };
+ commandHandler.setReplyTextBundle(replyTextBundle);
+ }
+
+}
diff --git a/tags/1.2.3/src/test/java/org/mockftpserver/core/session/DefaultSessionTest.java b/tags/1.2.3/src/test/java/org/mockftpserver/core/session/DefaultSessionTest.java
new file mode 100644
index 0000000..63cadc0
--- /dev/null
+++ b/tags/1.2.3/src/test/java/org/mockftpserver/core/session/DefaultSessionTest.java
@@ -0,0 +1,454 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.core.session;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.InetAddress;
+import java.net.SocketTimeoutException;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.log4j.Logger;
+import org.mockftpserver.core.MockFtpServerException;
+import org.mockftpserver.core.command.Command;
+import org.mockftpserver.core.socket.StubServerSocket;
+import org.mockftpserver.core.socket.StubServerSocketFactory;
+import org.mockftpserver.core.socket.StubSocket;
+import org.mockftpserver.core.socket.StubSocketFactory;
+import org.mockftpserver.core.util.AssertFailedException;
+import org.mockftpserver.test.AbstractTest;
+
+/**
+ * Tests for the DefaultSession class
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public final class DefaultSessionTest extends AbstractTest {
+
+ private static final Logger LOG = Logger.getLogger(DefaultSessionTest.class);
+ private static final String DATA = "sample data 123";
+ private static final int PORT = 197;
+ private static final String NAME1 = "name1";
+ private static final String NAME2 = "name2";
+ private static final Object VALUE = "value";
+
+ private DefaultSession session;
+ private ByteArrayOutputStream outputStream;
+ private Map commandHandlerMap;
+ private StubSocket stubSocket;
+ private InetAddress clientHost;
+
+ /**
+ * Perform initialization before each test
+ *
+ * @see org.mockftpserver.test.AbstractTest#setUp()
+ */
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ commandHandlerMap = new HashMap();
+ outputStream = new ByteArrayOutputStream();
+ session = createDefaultSession("");
+ clientHost = InetAddress.getLocalHost();
+ }
+
+ /**
+ * @see org.mockftpserver.test.AbstractTest#tearDown()
+ */
+ protected void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ /**
+ * Test the Constructor when the control socket is null
+ */
+ public void testConstructor_NullControlSocket() {
+ try {
+ new DefaultSession(null, commandHandlerMap);
+ fail("Expected AssertFailedException");
+ }
+ catch (AssertFailedException expected) {
+ LOG.info("Expected: " + expected);
+ }
+ }
+
+ /**
+ * Test the Constructor when the command handler Map is null
+ */
+ public void testConstructor_NullCommandHandlerMap() {
+ try {
+ new DefaultSession(stubSocket, null);
+ fail("Expected AssertFailedException");
+ }
+ catch (AssertFailedException expected) {
+ LOG.info("Expected: " + expected);
+ }
+ }
+
+ /**
+ * Test the setClientDataPort() method
+ */
+ public void testSetClientDataPort() {
+ StubSocket stubSocket = createTestSocket("");
+ StubSocketFactory stubSocketFactory = new StubSocketFactory(stubSocket);
+ session.socketFactory = stubSocketFactory;
+ session.setClientDataPort(PORT);
+ session.setClientDataHost(clientHost);
+ session.openDataConnection();
+ assertEquals("data port", PORT, stubSocketFactory.requestedDataPort);
+ }
+
+ /**
+ * Test the setClientDataPort() method after the session was in passive data mode
+ */
+ public void testSetClientDataPort_AfterPassiveConnectionMode() throws IOException {
+ StubServerSocket stubServerSocket = new StubServerSocket(PORT);
+ StubServerSocketFactory stubServerSocketFactory = new StubServerSocketFactory(stubServerSocket);
+ session.serverSocketFactory = stubServerSocketFactory;
+
+ session.switchToPassiveMode();
+ assertFalse("server socket closed", stubServerSocket.isClosed());
+ assertNotNull("passiveModeDataSocket", session.passiveModeDataSocket);
+ session.setClientDataPort(PORT);
+
+ // Make sure that any passive mode connection info is cleared out
+ assertTrue("server socket closed", stubServerSocket.isClosed());
+ assertNull("passiveModeDataSocket should be null", session.passiveModeDataSocket);
+ }
+
+ /**
+ * Test the setClientHost() method
+ */
+ public void testSetClientHost() throws Exception {
+ StubSocket stubSocket = createTestSocket("");
+ StubSocketFactory stubSocketFactory = new StubSocketFactory(stubSocket);
+ session.socketFactory = stubSocketFactory;
+ session.setClientDataHost(clientHost);
+ session.openDataConnection();
+ assertEquals("client host", clientHost, stubSocketFactory.requestedHost);
+ }
+
+ /**
+ * Test the openDataConnection(), setClientDataPort() and setClientDataHost() methods
+ */
+ public void testOpenDataConnection() {
+ StubSocket stubSocket = createTestSocket("");
+ StubSocketFactory stubSocketFactory = new StubSocketFactory(stubSocket);
+ session.socketFactory = stubSocketFactory;
+
+ // Use default client data port
+ session.setClientDataHost(clientHost);
+ session.openDataConnection();
+ assertEquals("data port", DefaultSession.DEFAULT_CLIENT_DATA_PORT, stubSocketFactory.requestedDataPort);
+ assertEquals("client host", clientHost, stubSocketFactory.requestedHost);
+
+ // Set client data port explicitly
+ session.setClientDataPort(PORT);
+ session.setClientDataHost(clientHost);
+ session.openDataConnection();
+ assertEquals("data port", PORT, stubSocketFactory.requestedDataPort);
+ assertEquals("client host", clientHost, stubSocketFactory.requestedHost);
+ }
+
+ /**
+ * Test the OpenDataConnection method, when in passive mode and no incoming connection is
+ * initiated
+ */
+ public void testOpenDataConnection_PassiveMode_NoConnection() throws IOException {
+
+ StubServerSocket stubServerSocket = new StubServerSocket(PORT);
+ StubServerSocketFactory stubServerSocketFactory = new StubServerSocketFactory(stubServerSocket);
+ session.serverSocketFactory = stubServerSocketFactory;
+
+ session.switchToPassiveMode();
+
+ try {
+ session.openDataConnection();
+ fail("Expected MockFtpServerException");
+ }
+ catch (MockFtpServerException expected) {
+ LOG.info("Expected: " + expected);
+ assertSame("cause", SocketTimeoutException.class, expected.getCause().getClass());
+ }
+ }
+
+ /**
+ * Test the OpenDataConnection method, when the clientHost has not been set
+ */
+ public void testOpenDataConnection_NullClientHost() {
+ try {
+ session.openDataConnection();
+ fail("Expected AssertFailedException");
+ }
+ catch (AssertFailedException expected) {
+ LOG.info("Expected: " + expected);
+ }
+ }
+
+ /**
+ * Test the readData() method
+ */
+ public void testReadData() {
+ StubSocket stubSocket = createTestSocket(DATA);
+ session.socketFactory = new StubSocketFactory(stubSocket);
+ session.setClientDataHost(clientHost);
+
+ session.openDataConnection();
+ byte[] data = session.readData();
+ LOG.info("data=[" + new String(data) + "]");
+ assertEquals("data", DATA.getBytes(), data);
+ }
+
+ /**
+ * Test the readData() method after switching to passive mode
+ */
+ public void testReadData_PassiveMode() throws IOException {
+ StubSocket stubSocket = createTestSocket(DATA);
+ StubServerSocket stubServerSocket = new StubServerSocket(PORT, stubSocket);
+ StubServerSocketFactory stubServerSocketFactory = new StubServerSocketFactory(stubServerSocket);
+ session.serverSocketFactory = stubServerSocketFactory;
+
+ session.switchToPassiveMode();
+ session.openDataConnection();
+ byte[] data = session.readData();
+ LOG.info("data=[" + new String(data) + "]");
+ assertEquals("data", DATA.getBytes(), data);
+ }
+
+ /**
+ * Test the closeDataConnection() method
+ */
+ public void testCloseDataConnection() {
+ StubSocket stubSocket = createTestSocket(DATA);
+ session.socketFactory = new StubSocketFactory(stubSocket);
+
+ session.setClientDataHost(clientHost);
+ session.openDataConnection();
+ session.closeDataConnection();
+ assertTrue("client data socket should be closed", stubSocket.isClosed());
+ }
+
+ /**
+ * Test the switchToPassiveMode() method
+ */
+ public void testSwitchToPassiveMode() throws IOException {
+ StubServerSocket stubServerSocket = new StubServerSocket(PORT);
+ StubServerSocketFactory stubServerSocketFactory = new StubServerSocketFactory(stubServerSocket);
+ session.serverSocketFactory = stubServerSocketFactory;
+
+ assertNull("passiveModeDataSocket starts out null", session.passiveModeDataSocket);
+ int port = session.switchToPassiveMode();
+ assertSame("passiveModeDataSocket", stubServerSocket, session.passiveModeDataSocket);
+ assertEquals("port", PORT, port);
+ }
+
+ /**
+ * Test the getServerHost() method
+ */
+ public void testGetServerHost() {
+ assertEquals("host", DEFAULT_HOST, session.getServerHost());
+ }
+
+ /**
+ * Test the getClientHost() method when the session is not yet started
+ */
+ public void testGetClientHost_NotRunning() {
+ assertNull("null", session.getClientHost());
+ }
+
+ /**
+ * Test the parseCommand() method
+ */
+ public void testParseCommand() {
+ Command command = session.parseCommand("LIST");
+ assertEquals("command name", "LIST", command.getName());
+ assertEquals("command parameters", EMPTY, command.getParameters());
+
+ command = session.parseCommand("USER user123");
+ assertEquals("command name", "USER", command.getName());
+ assertEquals("command parameters", array("user123"), command.getParameters());
+
+ command = session.parseCommand("PORT 127,0,0,1,17,37");
+ assertEquals("command name", "PORT", command.getName());
+ assertEquals("command parameters", new String[] { "127", "0", "0", "1", "17", "37" }, command
+ .getParameters());
+ }
+
+ /**
+ * Test the parseCommand() method, passing in an empty command String
+ */
+ public void testParseCommand_EmptyCommandString() {
+ try {
+ session.parseCommand("");
+ fail("Expected AssertFailedException");
+ }
+ catch (AssertFailedException expected) {
+ LOG.info("Expected: " + expected);
+ }
+ }
+
+ /**
+ * Test the sendData() method, as well as the openDataConnection() and closeDataConnection()
+ */
+ public void testSendData() {
+ StubSocket stubSocket = createTestSocket("1234567890 abcdef");
+ session.socketFactory = new StubSocketFactory(stubSocket);
+
+ session.setClientDataHost(clientHost);
+ session.openDataConnection();
+ session.sendData(DATA.getBytes(), DATA.length());
+ LOG.info("output=[" + outputStream.toString() + "]");
+ assertEquals("output", DATA, outputStream.toString());
+ }
+
+ /**
+ * Test the SendData() method, passing in a null byte[]
+ */
+ public void testSendData_Null() {
+
+ try {
+ session.sendData(null, 1);
+ fail("Expected AssertFailedException");
+ }
+ catch (AssertFailedException expected) {
+ LOG.info("Expected: " + expected);
+ }
+ }
+
+ /**
+ * Test the SendReply(int,String) method, passing in an invalid reply code
+ */
+ public void testSendReply_InvalidReplyCode() {
+
+ try {
+ session.sendReply(-66, "text");
+ fail("Expected AssertFailedException");
+ }
+ catch (AssertFailedException expected) {
+ LOG.info("Expected: " + expected);
+ }
+ }
+
+ /**
+ * Test the getAttribute() and setAttribute() methods
+ */
+ public void testGetAndSetAttribute() {
+ assertNull("name does not exist yet", session.getAttribute(NAME1));
+ session.setAttribute(NAME1, VALUE);
+ session.setAttribute(NAME2, null);
+ assertEquals("NAME1", VALUE, session.getAttribute(NAME1));
+ assertNull("NAME2", session.getAttribute(NAME2));
+ assertNull("no such name", session.getAttribute("noSuchName"));
+ }
+
+ /**
+ * Test the getAttribute() method, passing in a null name
+ */
+ public void testGetAttribute_Null() {
+ try {
+ session.getAttribute(null);
+ fail("Expected AssertFailedException");
+ }
+ catch (AssertFailedException expected) {
+ LOG.info("Expected: " + expected);
+ }
+ }
+
+ /**
+ * Test the setAttribute() method, passing in a null name
+ */
+ public void testSetAttribute_NullName() {
+ try {
+ session.setAttribute(null, VALUE);
+ fail("Expected AssertFailedException");
+ }
+ catch (AssertFailedException expected) {
+ LOG.info("Expected: " + expected);
+ }
+ }
+
+ /**
+ * Test the removeAttribute()
+ */
+ public void testRemoveAttribute() {
+ session.removeAttribute("noSuchName"); // do nothing
+ session.setAttribute(NAME1, VALUE);
+ session.removeAttribute(NAME1);
+ assertNull("NAME1", session.getAttribute(NAME1));
+ }
+
+ /**
+ * Test the removeAttribute() method, passing in a null name
+ */
+ public void testRemoveAttribute_Null() {
+ try {
+ session.removeAttribute(null);
+ fail("Expected AssertFailedException");
+ }
+ catch (AssertFailedException expected) {
+ LOG.info("Expected: " + expected);
+ }
+ }
+
+ /**
+ * Test the getAttributeNames()
+ */
+ public void testGetAttributeNames() {
+ assertEquals("No names yet", Collections.EMPTY_SET, session.getAttributeNames());
+ session.setAttribute(NAME1, VALUE);
+ assertEquals("1", Collections.singleton(NAME1), session.getAttributeNames());
+ session.setAttribute(NAME2, VALUE);
+ assertEquals("2", set(NAME1, NAME2), session.getAttributeNames());
+ }
+
+ // -------------------------------------------------------------------------
+ // Internal Helper Methods
+ // -------------------------------------------------------------------------
+
+ /**
+ * Create and return a DefaultSession object that reads from an InputStream with the specified
+ * contents and writes to the predefined outputStrean ByteArrayOutputStream. Also, save the
+ * StubSocket being used in the stubSocket attribute.
+ *
+ * @param inputStreamContents - the contents of the input stream
+ * @return the DefaultSession
+ */
+ private DefaultSession createDefaultSession(String inputStreamContents) {
+ stubSocket = createTestSocket(inputStreamContents);
+ return new DefaultSession(stubSocket, commandHandlerMap);
+ }
+
+ /**
+ * Create and return a StubSocket that reads from an InputStream with the specified contents and
+ * writes to the predefined outputStrean ByteArrayOutputStream.
+ *
+ * @param inputStreamContents - the contents of the input stream
+ * @return the StubSocket
+ */
+ private StubSocket createTestSocket(String inputStreamContents) {
+ InputStream inputStream = new ByteArrayInputStream(inputStreamContents.getBytes());
+ StubSocket stubSocket = new StubSocket(inputStream, outputStream);
+ stubSocket._setLocalAddress(DEFAULT_HOST);
+ return stubSocket;
+ }
+
+}
diff --git a/tags/1.2.3/src/test/java/org/mockftpserver/core/session/DefaultSession_RunTest.java b/tags/1.2.3/src/test/java/org/mockftpserver/core/session/DefaultSession_RunTest.java
new file mode 100644
index 0000000..9cc8eb1
--- /dev/null
+++ b/tags/1.2.3/src/test/java/org/mockftpserver/core/session/DefaultSession_RunTest.java
@@ -0,0 +1,246 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.core.session;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.ListResourceBundle;
+import java.util.Map;
+import java.util.ResourceBundle;
+
+
+import org.apache.log4j.Logger;
+import org.mockftpserver.core.command.Command;
+import org.mockftpserver.core.command.CommandHandler;
+import org.mockftpserver.core.command.CommandNames;
+import org.mockftpserver.core.command.InvocationRecord;
+import org.mockftpserver.core.session.DefaultSession;
+import org.mockftpserver.core.session.Session;
+import org.mockftpserver.core.socket.StubSocket;
+import org.mockftpserver.stub.command.AbstractStubCommandHandler;
+import org.mockftpserver.stub.command.ConnectCommandHandler;
+import org.mockftpserver.test.AbstractTest;
+
+/**
+ * Tests for the DefaultSession class that require the session (thread) to be running/active.
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public final class DefaultSession_RunTest extends AbstractTest {
+
+ private static final Logger LOG = Logger.getLogger(DefaultSession_RunTest.class);
+ private static final Command COMMAND = new Command("USER", EMPTY);
+ private static final int REPLY_CODE = 100;
+ private static final String REPLY_TEXT = "sample text description";
+
+ private DefaultSession session;
+ private ByteArrayOutputStream outputStream;
+ private Map commandHandlerMap;
+ private StubSocket stubSocket;
+ private boolean commandHandled = false;
+
+ /**
+ * Perform initialization before each test
+ *
+ * @see org.mockftpserver.test.AbstractTest#setUp()
+ */
+ protected void setUp() throws Exception {
+ super.setUp();
+ commandHandlerMap = new HashMap();
+ outputStream = new ByteArrayOutputStream();
+ }
+
+ /**
+ * Test that the CommandHandler is properly initialized and passed the expected parameters
+ */
+ public void testInvocationOfCommandHandler() throws Exception {
+ AbstractStubCommandHandler commandHandler = new AbstractStubCommandHandler() {
+ public void handleCommand(Command command, Session cmdSession, InvocationRecord invocationRecord) {
+ assertEquals("command", COMMAND, command);
+ assertSame("session", session, cmdSession);
+ assertEquals("InvocationRecord: command", COMMAND, invocationRecord.getCommand());
+ assertEquals("InvocationRecord: clientHost", DEFAULT_HOST, invocationRecord.getClientHost());
+ commandHandled = true;
+ }
+ };
+ runCommandAndVerifyOutput(commandHandler, "");
+ }
+
+ /**
+ * Test the close() method
+ *
+ * @throws IOException
+ */
+ public void testClose() throws Exception {
+ CommandHandler commandHandler = new AbstractStubCommandHandler() {
+ public void handleCommand(Command command, Session session, InvocationRecord invocationRecord) {
+ session.close();
+ commandHandled = true;
+ }
+ };
+ runCommandAndVerifyOutput(commandHandler, "");
+ assertFalse("socket should not be closed", stubSocket.isClosed());
+ }
+
+ /**
+ * Test the getClientHost() method
+ *
+ * @throws IOException
+ */
+ public void testGetClientHost() throws Exception {
+ CommandHandler commandHandler = new AbstractStubCommandHandler() {
+ public void handleCommand(Command command, Session session, InvocationRecord invocationRecord) {
+ commandHandled = true;
+ }
+ };
+ runCommandAndVerifyOutput(commandHandler, "");
+ LOG.info("clientHost=" + session.getClientHost());
+ assertEquals("clientHost", DEFAULT_HOST, session.getClientHost());
+ }
+
+ /**
+ * Test the sendReply() method, when the specified reply text is null
+ */
+ public void testSendReply_NullReplyText() throws Exception {
+ CommandHandler commandHandler = new AbstractStubCommandHandler() {
+ public void handleCommand(Command command, Session session, InvocationRecord invocationRecord) {
+ session.sendReply(REPLY_CODE, null);
+ commandHandled = true;
+ }
+ };
+ runCommandAndVerifyOutput(commandHandler, Integer.toString(REPLY_CODE));
+ }
+
+ /**
+ * Test the sendReply() method, verifying that extra extra whitespace is trimmed from the reply text
+ */
+ public void testSendReply_TrimReplyText() throws Exception {
+ CommandHandler commandHandler = new AbstractStubCommandHandler() {
+ public void handleCommand(Command command, Session session, InvocationRecord invocationRecord) {
+ session.sendReply(REPLY_CODE, " " + REPLY_TEXT + " ");
+ commandHandled = true;
+ }
+ };
+ runCommandAndVerifyOutput(commandHandler, REPLY_CODE + " " + REPLY_TEXT);
+ }
+
+ /**
+ * Test the sendReply() method, when the text contains multiple lines
+ */
+ public void testSendReply_MultiLineText() throws Exception {
+ final String MULTILINE_REPLY_TEXT = "abc\ndef\nghi\njkl";
+ final String FORMATTED_MULTILINE_REPLY_TEXT = "123-abc\ndef\nghi\n123 jkl";
+
+ CommandHandler commandHandler = new AbstractStubCommandHandler() {
+ public void handleCommand(Command command, Session session, InvocationRecord invocationRecord) {
+ session.sendReply(123, MULTILINE_REPLY_TEXT);
+ commandHandled = true;
+ }
+ };
+ runCommandAndVerifyOutput(commandHandler, FORMATTED_MULTILINE_REPLY_TEXT);
+ }
+
+ /**
+ * Test the sendReply() method when the reply code has associated reply text
+ */
+ public void testSendReply_ReplyText() throws Exception {
+ CommandHandler commandHandler = new AbstractStubCommandHandler() {
+ public void handleCommand(Command command, Session session, InvocationRecord invocationRecord) {
+ session.sendReply(REPLY_CODE, REPLY_TEXT);
+ commandHandled = true;
+ }
+ };
+ runCommandAndVerifyOutput(commandHandler, REPLY_CODE + " " + REPLY_TEXT);
+ }
+
+ // -------------------------------------------------------------------------
+ // Internal Helper Methods
+ // -------------------------------------------------------------------------
+
+ /**
+ * Create and return a DefaultSession object that reads from an InputStream with the specified
+ * contents and writes to the predefined outputStrean ByteArrayOutputStream. Also, save the
+ * StubSocket being used in the stubSocket attribute.
+ *
+ * @param inputStreamContents - the contents of the input stream
+ * @return the DefaultSession
+ */
+ private DefaultSession createDefaultSession(CommandHandler commandHandler) {
+ stubSocket = createTestSocket(COMMAND.getName());
+ commandHandlerMap.put(COMMAND.getName(), commandHandler);
+
+ ConnectCommandHandler connectCommandHandler = new ConnectCommandHandler();
+
+ ResourceBundle replyTextBundle = new ListResourceBundle() {
+ protected Object[][] getContents() {
+ return new Object[][] {
+ { "220", "Reply for 220" },
+ };
+ }
+ };
+ connectCommandHandler.setReplyTextBundle(replyTextBundle);
+ commandHandlerMap.put(CommandNames.CONNECT, connectCommandHandler);
+
+ return new DefaultSession(stubSocket, commandHandlerMap);
+ }
+
+ /**
+ * Create and return a StubSocket that reads from an InputStream with the specified contents and
+ * writes to the predefined outputStrean ByteArrayOutputStream.
+ *
+ * @param inputStreamContents - the contents of the input stream
+ * @return the StubSocket
+ */
+ private StubSocket createTestSocket(String inputStreamContents) {
+ InputStream inputStream = new ByteArrayInputStream(inputStreamContents.getBytes());
+ return new StubSocket(DEFAULT_HOST, inputStream, outputStream);
+ }
+
+ /**
+ * Run the command represented by the CommandHandler and verify that the session output from the
+ * control socket contains the expected output text.
+ *
+ * @param commandHandler - the CommandHandler to invoke
+ * @param expectedOutput - the text expected within the session output
+ * @throws InterruptedException
+ */
+ private void runCommandAndVerifyOutput(CommandHandler commandHandler, String expectedOutput)
+ throws InterruptedException {
+ session = createDefaultSession(commandHandler);
+
+ Thread thread = new Thread(session);
+ thread.start();
+
+ for (int i = 0; !commandHandled && i < 10; i++) {
+ Thread.sleep(50L);
+ }
+
+ session.close();
+ thread.join();
+
+ assertEquals("commandHandled", true, commandHandled);
+
+ String output = outputStream.toString().trim();
+ LOG.info("output=[" + output + "]");
+ assertTrue("output: expected [" + expectedOutput + "]", output.indexOf(expectedOutput) != -1);
+ }
+
+}
diff --git a/tags/1.2.3/src/test/java/org/mockftpserver/core/socket/StubServerSocket.java b/tags/1.2.3/src/test/java/org/mockftpserver/core/socket/StubServerSocket.java
new file mode 100644
index 0000000..e3a9686
--- /dev/null
+++ b/tags/1.2.3/src/test/java/org/mockftpserver/core/socket/StubServerSocket.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.core.socket;
+
+import java.io.IOException;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.SocketTimeoutException;
+
+/**
+ * Test (fake) subclass of ServerSocket that performs no network access and allows setting the
+ * Socket returned by accept(), and the local port for the ServerSocket.
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public class StubServerSocket extends ServerSocket {
+ private int localPort;
+ private Socket socket;
+
+ /**
+ * Construct a new instance with the specified local port.
+ * @param localPort - the local port to be returned from getLocalPort()
+ * @throws IOException
+ */
+ public StubServerSocket(int localPort) throws IOException {
+ this(localPort, null);
+ }
+
+ /**
+ * Construct a new instance with specified local port and accept() socket.
+ * @param localPort - the local port to be returned from getLocalPort()
+ * @param socket - the socket to be returned from accept(); if null, then accept() throws SocketTimeoutException.
+ * @throws IOException
+ */
+ public StubServerSocket(int localPort, Socket socket) throws IOException {
+ super(0);
+ this.localPort = localPort;
+ this.socket = socket;
+ }
+
+ /**
+ * Return the predefined local port
+ * @see java.net.ServerSocket#getLocalPort()
+ */
+ public int getLocalPort() {
+ return localPort;
+ }
+
+ /**
+ * If a socket was specified on the constructor, then return that; otherwise, throw a SocketTimeoutException.
+ * @see java.net.ServerSocket#accept()
+ */
+ public Socket accept() throws IOException {
+ if (socket != null) {
+ return socket;
+ }
+ throw new SocketTimeoutException();
+ }
+}
diff --git a/tags/1.2.3/src/test/java/org/mockftpserver/core/socket/StubServerSocketFactory.java b/tags/1.2.3/src/test/java/org/mockftpserver/core/socket/StubServerSocketFactory.java
new file mode 100644
index 0000000..33cc76d
--- /dev/null
+++ b/tags/1.2.3/src/test/java/org/mockftpserver/core/socket/StubServerSocketFactory.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.core.socket;
+
+import java.io.IOException;
+import java.net.ServerSocket;
+
+import org.mockftpserver.core.socket.ServerSocketFactory;
+
+/**
+ * Test-only implementation of ServerSocketFactory. It always returns the predefined
+ * ServerSocket instance specified on the constructor.
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public class StubServerSocketFactory implements ServerSocketFactory {
+ private StubServerSocket stubServerSocket;
+
+ /**
+ * Construct a new factory instance that always returns the specified
+ * ServerSocket instance.
+ * @param serverSocket - the ServerSocket instance to be returned by this factory
+ */
+ public StubServerSocketFactory(StubServerSocket serverSocket) {
+ this.stubServerSocket = serverSocket;
+ }
+
+ /**
+ * Return the predefined ServerSocket instance.
+ * @see org.mockftpserver.core.socket.ServerSocketFactory#createServerSocket(int)
+ */
+ public ServerSocket createServerSocket(int port) throws IOException {
+ return stubServerSocket;
+ }
+} \ No newline at end of file
diff --git a/tags/1.2.3/src/test/java/org/mockftpserver/core/socket/StubSocket.java b/tags/1.2.3/src/test/java/org/mockftpserver/core/socket/StubSocket.java
new file mode 100644
index 0000000..3816bf0
--- /dev/null
+++ b/tags/1.2.3/src/test/java/org/mockftpserver/core/socket/StubSocket.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.core.socket;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.InetAddress;
+import java.net.Socket;
+
+/**
+ * Test (fake) subclass of Socket that performs no network access and allows setting the
+ * inputStream and OutputStream for the socket.
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public final class StubSocket extends Socket {
+
+ private InetAddress inetAddress;
+ private InetAddress localAddress;
+ private InputStream inputStream;
+ private OutputStream outputStream;
+
+ /**
+ * Construct a new instance using the specified InputStream and OutputStream
+ * @param inputStream - the InputStream to use
+ * @param outputStream - the OutputStream to use
+ */
+ public StubSocket(InputStream inputStream, OutputStream outputStream) {
+ this(null, inputStream, outputStream);
+ }
+
+ /**
+ * Construct a new instance using the specified host, InputStream and OutputStream
+ * @param inetAddress - the InetAddress for this socket
+ * @param inputStream - the InputStream to use
+ * @param outputStream - the OutputStream to use
+ */
+ public StubSocket(InetAddress inetAddress, InputStream inputStream, OutputStream outputStream) {
+ this.inetAddress = inetAddress;
+ this.inputStream = inputStream;
+ this.outputStream = outputStream;
+ }
+
+ /**
+ * Override the superclass implementation. If the local inetAddress is not null,
+ * return that. Otherwise return super.getInetAddress().
+ * @see java.net.Socket#getInetAddress()
+ */
+ public InetAddress getInetAddress() {
+ return (inetAddress != null) ? inetAddress : super.getInetAddress();
+ }
+
+ /**
+ * Override the superclass implementation. If the local localAddress is not
+ * null, return that. Otherwise return super.getLocalAddress();
+ * @see java.net.Socket#getLocalAddress()
+ */
+ public InetAddress getLocalAddress() {
+ return (localAddress != null) ? localAddress : super.getLocalAddress();
+ }
+
+ /**
+ * Override the superclass implementation to provide the predefined InputStream
+ * @see java.net.Socket#getInputStream()
+ */
+ public InputStream getInputStream() throws IOException {
+ return inputStream;
+ }
+
+ /**
+ * Override the superclass implementation to provide the predefined OutputStream
+ * @see java.net.Socket#getOutputStream()
+ */
+ public OutputStream getOutputStream() throws IOException {
+ return outputStream;
+ }
+
+ //-------------------------------------------------------------------------
+ // Test-specific helper methods
+ //-------------------------------------------------------------------------
+
+ public void _setLocalAddress(InetAddress localAddress) {
+ this.localAddress = localAddress;
+ }
+}
diff --git a/tags/1.2.3/src/test/java/org/mockftpserver/core/socket/StubSocketFactory.java b/tags/1.2.3/src/test/java/org/mockftpserver/core/socket/StubSocketFactory.java
new file mode 100644
index 0000000..e5c8f1d
--- /dev/null
+++ b/tags/1.2.3/src/test/java/org/mockftpserver/core/socket/StubSocketFactory.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.core.socket;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.Socket;
+
+import org.mockftpserver.core.socket.SocketFactory;
+
+/**
+ * Test-only implementation of SocketFactory. It always returns the predefined
+ * StubSocket instance specified on the constructor. It allows direct access to the
+ * requested host address and port number.
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public class StubSocketFactory implements SocketFactory {
+ private StubSocket stubSocket;
+ public int requestedDataPort;
+ public InetAddress requestedHost;
+
+ /**
+ * Create a new instance that always returns the specified StubSocket instance.
+ * @param stubSocket - the StubSocket to be returned by this factory
+ */
+ public StubSocketFactory(StubSocket stubSocket) {
+ this.stubSocket = stubSocket;
+ }
+
+ /**
+ * Return the predefined StubSocket instance
+ * @see org.mockftpserver.core.socket.SocketFactory#createSocket(java.net.InetAddress, int)
+ */
+ public Socket createSocket(InetAddress host, int port) throws IOException {
+ this.requestedHost = host;
+ this.requestedDataPort = port;
+ return stubSocket;
+ }
+} \ No newline at end of file
diff --git a/tags/1.2.3/src/test/java/org/mockftpserver/core/util/AssertTest.java b/tags/1.2.3/src/test/java/org/mockftpserver/core/util/AssertTest.java
new file mode 100644
index 0000000..8c2eb71
--- /dev/null
+++ b/tags/1.2.3/src/test/java/org/mockftpserver/core/util/AssertTest.java
@@ -0,0 +1,230 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.core.util;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+
+import org.apache.log4j.Logger;
+import org.mockftpserver.core.util.Assert;
+import org.mockftpserver.core.util.AssertFailedException;
+import org.mockftpserver.test.AbstractTest;
+
+/**
+ * Tests for the Assert class
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public class AssertTest extends AbstractTest {
+
+ private static final Logger LOG = Logger.getLogger(AssertTest.class);
+
+ /**
+ * This interface defines a generic closure (a generic wrapper for a block of code).
+ */
+ private static interface ExceptionClosure {
+ /**
+ * Execute arbitrary logic that can throw any type of Exception
+ * @throws Exception
+ */
+ public void execute() throws Exception;
+ }
+
+
+ private static final String MESSAGE = "exception message";
+
+ /**
+ * Test the assertNull() method
+ */
+ public void testAssertNull() {
+
+ Assert.isNull(null, MESSAGE);
+
+ try {
+ Assert.isNull("OK", MESSAGE);
+ fail("Expected IllegalArumentException");
+ }
+ catch (AssertFailedException expected) {
+ LOG.info("Expected: " + expected);
+ assertExceptionMessageContains(expected, MESSAGE);
+ }
+ }
+
+
+ /**
+ * Test the assertNotNull() method
+ */
+ public void testAssertNotNull() {
+
+ Assert.notNull("OK", MESSAGE);
+
+ try {
+ Assert.notNull(null, MESSAGE);
+ fail("Expected IllegalArumentException");
+ }
+ catch (AssertFailedException expected) {
+ LOG.info("Expected: " + expected);
+ assertExceptionMessageContains(expected, MESSAGE);
+ }
+ }
+
+ /**
+ * Test the assertTrue() method
+ */
+ public void testAssertTrue() throws Exception {
+
+ Assert.isTrue(true, MESSAGE);
+
+ verifyThrowsAssertFailedException(true, new ExceptionClosure() {
+ public void execute() throws Exception {
+ Assert.isTrue(false, MESSAGE);
+ }
+ });
+ }
+
+ /**
+ * Test the assertFalse() method
+ */
+ public void testAssertFalse() throws Exception {
+
+ Assert.isFalse(false, MESSAGE);
+
+ verifyThrowsAssertFailedException(true, new ExceptionClosure() {
+ public void execute() throws Exception {
+ Assert.isFalse(true, MESSAGE);
+ }
+ });
+ }
+
+ /**
+ * Test the assertNotEmpty(Collection,String) method
+ */
+ public void testAssertNotNullOrEmpty_Collection() throws Exception {
+
+ final Collection COLLECTION = Collections.singletonList("item");
+ Assert.notNullOrEmpty(COLLECTION, MESSAGE);
+
+ verifyThrowsAssertFailedException(true, new ExceptionClosure() {
+ public void execute() throws Exception {
+ Assert.notNullOrEmpty((Collection) null, MESSAGE);
+ }
+ });
+
+ verifyThrowsAssertFailedException(true, new ExceptionClosure() {
+ public void execute() throws Exception {
+ Assert.notNullOrEmpty(new ArrayList(), MESSAGE);
+ }
+ });
+ }
+
+ /**
+ * Test the assertNotEmpty(Map,String) method
+ */
+ public void testAssertNotNullOrEmpty_Map() throws Exception {
+
+ final Map MAP = Collections.singletonMap("key", "value");
+ Assert.notNullOrEmpty(MAP, MESSAGE);
+
+ verifyThrowsAssertFailedException(true, new ExceptionClosure() {
+ public void execute() throws Exception {
+ Assert.notNullOrEmpty((Map) null, MESSAGE);
+ }
+ });
+
+ verifyThrowsAssertFailedException(true, new ExceptionClosure() {
+ public void execute() throws Exception {
+ Assert.notNullOrEmpty(new HashMap(), MESSAGE);
+ }
+ });
+ }
+
+ /**
+ * Test the assertNotEmpty(Objecct[],String) method
+ */
+ public void testAssertNotNullOrEmpty_array() throws Exception {
+
+ final Object[] ARRAY = { "1", "2" };
+ Assert.notNullOrEmpty(ARRAY, MESSAGE);
+
+ verifyThrowsAssertFailedException(true, new ExceptionClosure() {
+ public void execute() throws Exception {
+ Assert.notNullOrEmpty((Object[]) null, MESSAGE);
+ }
+ });
+
+ verifyThrowsAssertFailedException(true, new ExceptionClosure() {
+ public void execute() throws Exception {
+ Assert.notNullOrEmpty(new String[] { }, MESSAGE);
+ }
+ });
+ }
+
+ /**
+ * Test the assertNotEmpty(String,String) method
+ */
+ public void testAssertNotNullOrEmpty_String() throws Exception {
+
+ Assert.notNullOrEmpty("OK", MESSAGE);
+
+ verifyThrowsAssertFailedException(true, new ExceptionClosure() {
+ public void execute() throws Exception {
+ Assert.notNullOrEmpty((String) null, MESSAGE);
+ }
+ });
+
+ verifyThrowsAssertFailedException(true, new ExceptionClosure() {
+ public void execute() throws Exception {
+ Assert.notNullOrEmpty("", MESSAGE);
+ }
+ });
+ }
+
+ //-------------------------------------------------------------------------
+ // Helper Methods
+ //-------------------------------------------------------------------------
+
+ private void assertExceptionMessageContains(Throwable exception, String text) {
+ String message = exception.getMessage();
+ assertTrue("Exception message [" + message + "] does not contain [" + text + "]", message.indexOf(text) != -1);
+ }
+
+ /**
+ * Verify that execution of the ExceptionClosure (code block) results in an
+ * AssertFailedException being thrown with the constant MESSAGE as its message.
+ * @param closure - the ExceptionClosure encapsulating the code to execute
+ */
+ private void verifyThrowsAssertFailedException(boolean checkMessage, ExceptionClosure closure)
+ throws Exception {
+
+ try {
+ closure.execute();
+ fail("Expected IllegalArumentException");
+ }
+ catch (AssertFailedException expected) {
+ LOG.info("Expected: " + expected);
+ if (checkMessage) {
+ assertExceptionMessageContains(expected, MESSAGE);
+ }
+ }
+ }
+
+}
diff --git a/tags/1.2.3/src/test/java/org/mockftpserver/stub/StubFtpServerIntegrationTest.java b/tags/1.2.3/src/test/java/org/mockftpserver/stub/StubFtpServerIntegrationTest.java
new file mode 100644
index 0000000..b4a443b
--- /dev/null
+++ b/tags/1.2.3/src/test/java/org/mockftpserver/stub/StubFtpServerIntegrationTest.java
@@ -0,0 +1,639 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.stub;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+
+import org.apache.commons.net.ftp.FTP;
+import org.apache.commons.net.ftp.FTPClient;
+import org.apache.commons.net.ftp.FTPFile;
+import org.apache.log4j.Logger;
+import org.mockftpserver.core.command.CommandHandler;
+import org.mockftpserver.core.command.CommandNames;
+import org.mockftpserver.core.command.InvocationRecord;
+import org.mockftpserver.core.command.SimpleCompositeCommandHandler;
+import org.mockftpserver.core.command.StaticReplyCommandHandler;
+import org.mockftpserver.stub.StubFtpServer;
+import org.mockftpserver.stub.command.AppeCommandHandler;
+import org.mockftpserver.stub.command.CwdCommandHandler;
+import org.mockftpserver.stub.command.HelpCommandHandler;
+import org.mockftpserver.stub.command.ListCommandHandler;
+import org.mockftpserver.stub.command.NlstCommandHandler;
+import org.mockftpserver.stub.command.PwdCommandHandler;
+import org.mockftpserver.stub.command.RetrCommandHandler;
+import org.mockftpserver.stub.command.StatCommandHandler;
+import org.mockftpserver.stub.command.StorCommandHandler;
+import org.mockftpserver.stub.command.StouCommandHandler;
+import org.mockftpserver.test.AbstractTest;
+import org.mockftpserver.test.IntegrationTest;
+import org.mockftpserver.test.PortTestUtil;
+
+/**
+ * Tests for StubFtpServer using the Apache Jakarta Commons Net FTP client.
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public final class StubFtpServerIntegrationTest extends AbstractTest implements IntegrationTest {
+
+ private static final Logger LOG = Logger.getLogger(StubFtpServerIntegrationTest.class);
+ private static final String SERVER = "localhost";
+ private static final String USERNAME = "user123";
+ private static final String PASSWORD = "password";
+ private static final String FILENAME = "abc.txt";
+ private static final String ASCII_CONTENTS = "abcdef\tghijklmnopqr";
+ private static final byte[] BINARY_CONTENTS = new byte[256];
+
+ private StubFtpServer stubFtpServer;
+ private FTPClient ftpClient;
+ private RetrCommandHandler retrCommandHandler;
+ private StorCommandHandler storCommandHandler;
+
+ //-------------------------------------------------------------------------
+ // Tests
+ //-------------------------------------------------------------------------
+
+ /**
+ * Test connecting and logging in
+ */
+ public void testLogin() throws Exception {
+ // Connect
+ LOG.info("Conecting to " + SERVER);
+ ftpClientConnect();
+ verifyReplyCode("connect", 220);
+
+ // Login
+ String userAndPassword = USERNAME + "/" + PASSWORD;
+ LOG.info("Logging in as " + userAndPassword);
+ boolean success = ftpClient.login(USERNAME, PASSWORD);
+ assertTrue("Unable to login with " + userAndPassword, success);
+ verifyReplyCode("login with " + userAndPassword, 230);
+
+ // Quit
+ LOG.info("Quit");
+ ftpClient.quit();
+ verifyReplyCode("quit", 221);
+ }
+
+ /**
+ * Test the ACCT command
+ */
+ public void testAcct() throws Exception {
+ ftpClientConnect();
+
+ // ACCT
+ int replyCode = ftpClient.acct("123456");
+ assertEquals("acct", 230, replyCode);
+ }
+
+ /**
+ * Test the stop() method when no session has ever been started
+ */
+ public void testStop_NoSessionEverStarted() throws Exception {
+ LOG.info("Testing a stop() when no session has ever been started");
+ }
+
+ /**
+ * Test help (HELP)
+ */
+ public void testHelp() throws Exception {
+ // Modify HELP CommandHandler to return a predefined help message
+ final String HELP = "help message";
+ HelpCommandHandler helpCommandHandler = (HelpCommandHandler) stubFtpServer.getCommandHandler(CommandNames.HELP);
+ helpCommandHandler.setHelpMessage(HELP);
+
+ ftpClientConnect();
+
+ // HELP
+ String help = ftpClient.listHelp();
+ assertTrue("Wrong response", help.indexOf(HELP) != -1);
+ verifyReplyCode("listHelp", 214);
+ }
+
+ /**
+ * Test the LIST and SYST commands.
+ */
+ public void testList() throws Exception {
+ ftpClientConnect();
+
+ // Set directory listing
+ ListCommandHandler listCommandHandler = (ListCommandHandler) stubFtpServer.getCommandHandler(CommandNames.LIST);
+ listCommandHandler.setDirectoryListing("11-09-01 12:30PM 406348 File2350.log\n"
+ + "11-01-01 1:30PM <DIR> 0 archive");
+
+ // LIST
+ FTPFile[] files = ftpClient.listFiles();
+ assertEquals("number of files", 2, files.length);
+ verifyFTPFile(files[0], FTPFile.FILE_TYPE, "File2350.log", 406348L);
+ verifyFTPFile(files[1], FTPFile.DIRECTORY_TYPE, "archive", 0L);
+ verifyReplyCode("list", 226);
+ }
+
+ /**
+ * Test the LIST, PASV and SYST commands, transferring a directory listing in passive mode
+ */
+ public void testList_PassiveMode() throws Exception {
+ ftpClientConnect();
+
+ ftpClient.enterLocalPassiveMode();
+
+ // Set directory listing
+ ListCommandHandler listCommandHandler = (ListCommandHandler) stubFtpServer.getCommandHandler(CommandNames.LIST);
+ listCommandHandler.setDirectoryListing("11-09-01 12:30PM 406348 File2350.log");
+
+ // LIST
+ FTPFile[] files = ftpClient.listFiles();
+ assertEquals("number of files", 1, files.length);
+ verifyReplyCode("list", 226);
+ }
+
+ /**
+ * Test the NLST command.
+ */
+ public void testNlst() throws Exception {
+ ftpClientConnect();
+
+ // Set directory listing
+ NlstCommandHandler nlstCommandHandler = (NlstCommandHandler) stubFtpServer.getCommandHandler(CommandNames.NLST);
+ nlstCommandHandler.setDirectoryListing("File1.txt\nfile2.data");
+
+ // NLST
+ String[] filenames = ftpClient.listNames();
+ assertEquals("number of files", 2, filenames.length);
+ assertEquals(filenames[0], "File1.txt");
+ assertEquals(filenames[1], "file2.data");
+ verifyReplyCode("listNames", 226);
+ }
+
+ /**
+ * Test printing the current working directory (PWD)
+ */
+ public void testPwd() throws Exception {
+ // Modify PWD CommandHandler to return a predefined directory
+ final String DIR = "some/dir";
+ PwdCommandHandler pwdCommandHandler = (PwdCommandHandler) stubFtpServer.getCommandHandler(CommandNames.PWD);
+ pwdCommandHandler.setDirectory(DIR);
+
+ ftpClientConnect();
+
+ // PWD
+ String dir = ftpClient.printWorkingDirectory();
+ assertEquals("Unable to PWD", DIR, dir);
+ verifyReplyCode("printWorkingDirectory", 257);
+ }
+
+ /**
+ * Test getting the status (STAT)
+ */
+ public void testStat() throws Exception {
+ // Modify Stat CommandHandler to return predefined text
+ final String STATUS = "some information 123";
+ StatCommandHandler statCommandHandler = (StatCommandHandler) stubFtpServer.getCommandHandler(CommandNames.STAT);
+ statCommandHandler.setStatus(STATUS);
+
+ ftpClientConnect();
+
+ // STAT
+ String status = ftpClient.getStatus();
+ assertEquals("STAT reply", "211 " + STATUS + ".", status.trim());
+ verifyReplyCode("getStatus", 211);
+ }
+
+ /**
+ * Test getting the status (STAT), when the reply text contains multiple lines
+ */
+ public void testStat_MultilineReplyText() throws Exception {
+ // Modify Stat CommandHandler to return predefined text
+ final String STATUS = "System name: abc.def\nVersion 3.5.7\nNumber of failed logins: 2";
+ final String FORMATTED_REPLY_STATUS = "211-System name: abc.def\r\nVersion 3.5.7\r\n211 Number of failed logins: 2.";
+ StatCommandHandler statCommandHandler = (StatCommandHandler) stubFtpServer.getCommandHandler(CommandNames.STAT);
+ statCommandHandler.setStatus(STATUS);
+
+ ftpClientConnect();
+
+ // STAT
+ String status = ftpClient.getStatus();
+ assertEquals("STAT reply", FORMATTED_REPLY_STATUS, status.trim());
+ verifyReplyCode("getStatus", 211);
+ }
+
+ /**
+ * Test the System (SYST) command
+ */
+ public void testSyst() throws Exception {
+ ftpClientConnect();
+
+ // SYST
+ assertEquals("getSystemName()", "\"WINDOWS\" system type.", ftpClient.getSystemName());
+ verifyReplyCode("syst", 215);
+ }
+
+ /**
+ * Test changing the current working directory (CWD)
+ */
+ public void testCwd() throws Exception {
+ // Connect
+ LOG.info("Conecting to " + SERVER);
+ ftpClientConnect();
+ verifyReplyCode("connect", 220);
+
+ // CWD
+ boolean success = ftpClient.changeWorkingDirectory("dir1/dir2");
+ assertTrue("Unable to CWD", success);
+ verifyReplyCode("changeWorkingDirectory", 250);
+ }
+
+ /**
+ * Test changing the current working directory (CWD), when it causes a remote error
+ */
+ public void testCwd_Error() throws Exception {
+ // Override CWD CommandHandler to return error reply code
+ final int REPLY_CODE = 500;
+ StaticReplyCommandHandler cwdCommandHandler = new StaticReplyCommandHandler(REPLY_CODE);
+ stubFtpServer.setCommandHandler("CWD", cwdCommandHandler);
+
+ ftpClientConnect();
+
+ // CWD
+ boolean success = ftpClient.changeWorkingDirectory("dir1/dir2");
+ assertFalse("Expected failure", success);
+ verifyReplyCode("changeWorkingDirectory", REPLY_CODE);
+ }
+
+ /**
+ * Test changing to the parent directory (CDUP)
+ */
+ public void testCdup() throws Exception {
+ ftpClientConnect();
+
+ // CDUP
+ boolean success = ftpClient.changeToParentDirectory();
+ assertTrue("Unable to CDUP", success);
+ verifyReplyCode("changeToParentDirectory", 250);
+ }
+
+ /**
+ * Test delete (DELE)
+ */
+ public void testDele() throws Exception {
+ ftpClientConnect();
+
+ // DELE
+ boolean success = ftpClient.deleteFile(FILENAME);
+ assertTrue("Unable to DELE", success);
+ verifyReplyCode("deleteFile", 250);
+ }
+
+ /**
+ * Test make directory (MKD)
+ */
+ public void testMkd() throws Exception {
+ ftpClientConnect();
+
+ // MKD
+ boolean success = ftpClient.makeDirectory("dir1/dir2");
+ assertTrue("Unable to CWD", success);
+ verifyReplyCode("makeDirectory", 257);
+ }
+
+ /**
+ * Test NOOP
+ */
+ public void testNoop() throws Exception {
+ ftpClientConnect();
+
+ // NOOP
+ boolean success = ftpClient.sendNoOp();
+ assertTrue("Unable to NOOP", success);
+ verifyReplyCode("NOOP", 200);
+ }
+
+ /**
+ * Test restart (REST)
+ */
+ public void testRest() throws Exception {
+ ftpClientConnect();
+
+ // REST
+ int replyCode = ftpClient.rest("marker");
+ assertEquals("Unable to REST", 350, replyCode);
+ }
+
+ /**
+ * Test changing the current working directory (RMD)
+ */
+ public void testRmd() throws Exception {
+ ftpClientConnect();
+
+ // RMD
+ boolean success = ftpClient.removeDirectory("dir1/dir2");
+ assertTrue("Unable to RMD", success);
+ verifyReplyCode("removeDirectory", 250);
+ }
+
+ /**
+ * Test rename (RNFR/RNTO)
+ */
+ public void testRename() throws Exception {
+ ftpClientConnect();
+
+ // Rename (RNFR, RNTO)
+ boolean success = ftpClient.rename(FILENAME, "new_" + FILENAME);
+ assertTrue("Unable to RENAME", success);
+ verifyReplyCode("rename", 250);
+ }
+
+ /**
+ * Test the ALLO command
+ */
+ public void testAllo() throws Exception {
+ ftpClientConnect();
+
+ // ALLO
+ assertTrue("ALLO", ftpClient.allocate(1024));
+ assertTrue("ALLO with recordSize", ftpClient.allocate(1024, 64));
+ }
+
+ /**
+ * Test GET and PUT of ASCII files
+ */
+ public void testTransferAsciiFile() throws Exception {
+ retrCommandHandler.setFileContents(ASCII_CONTENTS);
+
+ ftpClientConnect();
+
+ // Get File
+ LOG.info("Get File for remotePath [" + FILENAME + "]");
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ assertTrue(ftpClient.retrieveFile(FILENAME, outputStream));
+ LOG.info("File contents=[" + outputStream.toString());
+ assertEquals("File contents", ASCII_CONTENTS, outputStream.toString());
+
+ // Put File
+ LOG.info("Put File for local path [" + FILENAME + "]");
+ ByteArrayInputStream inputStream = new ByteArrayInputStream(ASCII_CONTENTS.getBytes());
+ assertTrue(ftpClient.storeFile(FILENAME, inputStream));
+ InvocationRecord invocationRecord = storCommandHandler.getInvocation(0);
+ byte[] contents = (byte[]) invocationRecord.getObject(StorCommandHandler.FILE_CONTENTS_KEY);
+ LOG.info("File contents=[" + contents + "]");
+ assertEquals("File contents", ASCII_CONTENTS.getBytes(), contents);
+ }
+
+ /**
+ * Test GET and PUT of binary files
+ */
+ public void testTransferBinaryFiles() throws Exception {
+ retrCommandHandler.setFileContents(BINARY_CONTENTS);
+
+ ftpClientConnect();
+ ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
+
+ // Get File
+ LOG.info("Get File for remotePath [" + FILENAME + "]");
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ assertTrue("GET", ftpClient.retrieveFile(FILENAME, outputStream));
+ LOG.info("GET File length=" + outputStream.size());
+ assertEquals("File contents", BINARY_CONTENTS, outputStream.toByteArray());
+
+ // Put File
+ LOG.info("Put File for local path [" + FILENAME + "]");
+ ByteArrayInputStream inputStream = new ByteArrayInputStream(BINARY_CONTENTS);
+ assertTrue("PUT", ftpClient.storeFile(FILENAME, inputStream));
+ InvocationRecord invocationRecord = storCommandHandler.getInvocation(0);
+ byte[] contents = (byte[]) invocationRecord.getObject(StorCommandHandler.FILE_CONTENTS_KEY);
+ LOG.info("PUT File length=" + contents.length);
+ assertEquals("File contents", BINARY_CONTENTS, contents);
+ }
+
+ /**
+ * Test the STOU command
+ */
+ public void testStou() throws Exception {
+ StouCommandHandler stouCommandHandler = (StouCommandHandler) stubFtpServer.getCommandHandler(CommandNames.STOU);
+ stouCommandHandler.setFilename(FILENAME);
+
+ ftpClientConnect();
+
+ // Stor a File (STOU)
+ ByteArrayInputStream inputStream = new ByteArrayInputStream(ASCII_CONTENTS.getBytes());
+ assertTrue(ftpClient.storeUniqueFile(FILENAME, inputStream));
+ InvocationRecord invocationRecord = stouCommandHandler.getInvocation(0);
+ byte[] contents = (byte[]) invocationRecord.getObject(StorCommandHandler.FILE_CONTENTS_KEY);
+ LOG.info("File contents=[" + contents + "]");
+ assertEquals("File contents", ASCII_CONTENTS.getBytes(), contents);
+ }
+
+ /**
+ * Test the APPE command
+ */
+ public void testAppe() throws Exception {
+ AppeCommandHandler appeCommandHandler = (AppeCommandHandler) stubFtpServer.getCommandHandler(CommandNames.APPE);
+
+ ftpClientConnect();
+
+ // Append a File (APPE)
+ ByteArrayInputStream inputStream = new ByteArrayInputStream(ASCII_CONTENTS.getBytes());
+ assertTrue(ftpClient.appendFile(FILENAME, inputStream));
+ InvocationRecord invocationRecord = appeCommandHandler.getInvocation(0);
+ byte[] contents = (byte[]) invocationRecord.getObject(AppeCommandHandler.FILE_CONTENTS_KEY);
+ LOG.info("File contents=[" + contents + "]");
+ assertEquals("File contents", ASCII_CONTENTS.getBytes(), contents);
+ }
+
+ /**
+ * Test the ABOR command
+ */
+ public void testAbor() throws Exception {
+ ftpClientConnect();
+
+ // ABOR
+ assertTrue("ABOR", ftpClient.abort());
+ }
+
+ /**
+ * Test the Passive (PASV) command
+ */
+ public void testPasv() throws Exception {
+ ftpClientConnect();
+
+ // PASV
+ ftpClient.enterLocalPassiveMode();
+ // no reply code; the PASV command is sent only when the data connection is opened
+ }
+
+ /**
+ * Test Mode (MODE)
+ */
+ public void testMode() throws Exception {
+ ftpClientConnect();
+
+ // MODE
+ boolean success = ftpClient.setFileTransferMode(FTP.STREAM_TRANSFER_MODE);
+ assertTrue("Unable to MODE", success);
+ verifyReplyCode("setFileTransferMode", 200);
+ }
+
+ /**
+ * Test file structure (STRU)
+ */
+ public void testStru() throws Exception {
+ ftpClientConnect();
+
+ // STRU
+ boolean success = ftpClient.setFileStructure(FTP.FILE_STRUCTURE);
+ assertTrue("Unable to STRU", success);
+ verifyReplyCode("setFileStructure", 200);
+ }
+
+ /**
+ * Test the SimpleCompositeCommandHandler
+ */
+ public void testSimpleCompositeCommandHandler() throws Exception {
+ // Replace CWD CommandHandler with a SimpleCompositeCommandHandler
+ CommandHandler commandHandler1 = new StaticReplyCommandHandler(500);
+ CommandHandler commandHandler2 = new CwdCommandHandler();
+ SimpleCompositeCommandHandler simpleCompositeCommandHandler = new SimpleCompositeCommandHandler();
+ simpleCompositeCommandHandler.addCommandHandler(commandHandler1);
+ simpleCompositeCommandHandler.addCommandHandler(commandHandler2);
+ stubFtpServer.setCommandHandler("CWD", simpleCompositeCommandHandler);
+
+ // Connect
+ ftpClientConnect();
+
+ // CWD
+ assertFalse("first", ftpClient.changeWorkingDirectory("dir1/dir2"));
+ assertTrue("first", ftpClient.changeWorkingDirectory("dir1/dir2"));
+ }
+
+ /**
+ * Test site parameters (SITE)
+ */
+ public void testSite() throws Exception {
+ ftpClientConnect();
+
+ // SITE
+ int replyCode = ftpClient.site("parameters,1,2,3");
+ assertEquals("SITE", 200, replyCode);
+ }
+
+ /**
+ * Test structure mount (SMNT)
+ */
+ public void testSmnt() throws Exception {
+ ftpClientConnect();
+
+ // SMNT
+ assertTrue("SMNT", ftpClient.structureMount("dir1/dir2"));
+ verifyReplyCode("structureMount", 250);
+ }
+
+ /**
+ * Test reinitialize (REIN)
+ */
+ public void testRein() throws Exception {
+ ftpClientConnect();
+
+ // REIN
+ assertEquals("REIN", 220, ftpClient.rein());
+ }
+
+ /**
+ * Test that command names in lowercase or mixed upper/lower case are accepted
+ */
+ public void testCommandNamesInLowerOrMixedCase() throws Exception {
+ ftpClientConnect();
+
+ assertEquals("rein", 220, ftpClient.sendCommand("rein"));
+ assertEquals("rEIn", 220, ftpClient.sendCommand("rEIn"));
+ assertEquals("reiN", 220, ftpClient.sendCommand("reiN"));
+ assertEquals("Rein", 220, ftpClient.sendCommand("Rein"));
+ }
+
+ // -------------------------------------------------------------------------
+ // Test setup and tear-down
+ // -------------------------------------------------------------------------
+
+ /**
+ * Perform initialization before each test
+ * @see org.mockftpserver.test.AbstractTest#setUp()
+ */
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ for (int i = 0; i < BINARY_CONTENTS.length; i++) {
+ BINARY_CONTENTS[i] = (byte) i;
+ }
+
+ stubFtpServer = new StubFtpServer();
+ stubFtpServer.setServerControlPort(PortTestUtil.getFtpServerControlPort());
+ stubFtpServer.start();
+ ftpClient = new FTPClient();
+ retrCommandHandler = (RetrCommandHandler) stubFtpServer.getCommandHandler(CommandNames.RETR);
+ storCommandHandler = (StorCommandHandler) stubFtpServer.getCommandHandler(CommandNames.STOR);
+ }
+
+ /**
+ * Perform cleanup after each test
+ * @see org.mockftpserver.test.AbstractTest#tearDown()
+ */
+ protected void tearDown() throws Exception {
+ super.tearDown();
+ stubFtpServer.stop();
+ }
+
+ // -------------------------------------------------------------------------
+ // Internal Helper Methods
+ // -------------------------------------------------------------------------
+
+ /**
+ * Connect to the server from the FTPClient
+ */
+ private void ftpClientConnect() throws IOException {
+ ftpClient.connect(SERVER, PortTestUtil.getFtpServerControlPort());
+ }
+
+ /**
+ * Assert that the FtpClient reply code is equal to the expected value
+ *
+ * @param operation - the description of the operation performed; used in the error message
+ * @param expectedReplyCode - the expected FtpClient reply code
+ */
+ private void verifyReplyCode(String operation, int expectedReplyCode) {
+ int replyCode = ftpClient.getReplyCode();
+ LOG.info("Reply: operation=\"" + operation + "\" replyCode=" + replyCode);
+ assertEquals("Unexpected replyCode for " + operation, expectedReplyCode, replyCode);
+ }
+
+ /**
+ * Verify that the FTPFile has the specified properties
+ *
+ * @param ftpFile - the FTPFile to verify
+ * @param type - the expected file type
+ * @param name - the expected file name
+ * @param size - the expected file size (will be zero for a directory)
+ */
+ private void verifyFTPFile(FTPFile ftpFile, int type, String name, long size) {
+ LOG.info(ftpFile);
+ assertEquals("type: " + ftpFile, type, ftpFile.getType());
+ assertEquals("name: " + ftpFile, name, ftpFile.getName());
+ assertEquals("size: " + ftpFile, size, ftpFile.getSize());
+ }
+
+}
diff --git a/tags/1.2.3/src/test/java/org/mockftpserver/stub/StubFtpServerTest.java b/tags/1.2.3/src/test/java/org/mockftpserver/stub/StubFtpServerTest.java
new file mode 100644
index 0000000..eb7639d
--- /dev/null
+++ b/tags/1.2.3/src/test/java/org/mockftpserver/stub/StubFtpServerTest.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.stub;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.ResourceBundle;
+
+
+import org.apache.log4j.Logger;
+import org.mockftpserver.core.command.Command;
+import org.mockftpserver.core.command.CommandHandler;
+import org.mockftpserver.core.command.InvocationRecord;
+import org.mockftpserver.core.session.Session;
+import org.mockftpserver.core.util.AssertFailedException;
+import org.mockftpserver.stub.StubFtpServer;
+import org.mockftpserver.stub.command.AbstractStubCommandHandler;
+import org.mockftpserver.stub.command.CwdCommandHandler;
+import org.mockftpserver.test.AbstractTest;
+
+/**
+ * Unit tests for StubFtpServer. Also see {@link StubFtpServer_StartTest}
+ * and {@link StubFtpServerIntegrationTest}.
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public final class StubFtpServerTest extends AbstractTest {
+
+ private static final Logger LOG = Logger.getLogger(StubFtpServerTest.class);
+
+ private StubFtpServer stubFtpServer;
+ private AbstractStubCommandHandler commandHandler;
+ private CommandHandler commandHandler_NoReplyTextBundle;
+
+ /**
+ * Test the setCommandHandlers() method
+ */
+ public void testSetCommandHandlers() {
+ Map mapping = new HashMap();
+ mapping.put("AAA", commandHandler);
+ mapping.put("BBB", commandHandler_NoReplyTextBundle);
+
+ stubFtpServer.setCommandHandlers(mapping);
+ assertSame("commandHandler1", commandHandler, stubFtpServer.getCommandHandler("AAA"));
+ assertSame("commandHandler2", commandHandler_NoReplyTextBundle, stubFtpServer.getCommandHandler("BBB"));
+
+ assertSame("replyTextBundle", stubFtpServer.replyTextBundle, commandHandler.getReplyTextBundle());
+
+ // Make sure default CommandHandlers are still set
+ assertEquals("CwdCommandHandler", CwdCommandHandler.class, stubFtpServer.getCommandHandler("CWD").getClass());
+ }
+
+ /**
+ * Test the setCommandHandlers() method, when the Map is null
+ */
+ public void testSetCommandHandlers_Null() {
+ try {
+ stubFtpServer.setCommandHandlers(null);
+ fail("Expected AssertFailedException");
+ }
+ catch (AssertFailedException expected) {
+ LOG.info("Expected: " + expected);
+ }
+ }
+
+ /**
+ * Test the setCommandHandler() method, for a CommandHandler that does not implement ResourceBundleAware
+ */
+ public void testSetCommandHandler_NotReplyTextBundleAware() {
+ stubFtpServer.setCommandHandler("ZZZ", commandHandler_NoReplyTextBundle);
+ assertSame("commandHandler", commandHandler_NoReplyTextBundle, stubFtpServer.getCommandHandler("ZZZ"));
+ }
+
+ /**
+ * Test the setCommandHandler() method, for a CommandHandler that implements ReplyTextBundleAware,
+ * and whose replyTextBundle attribute is null.
+ */
+ public void testSetCommandHandler_NullReplyTextBundle() {
+ stubFtpServer.setCommandHandler("ZZZ", commandHandler);
+ assertSame("commandHandler", commandHandler, stubFtpServer.getCommandHandler("ZZZ"));
+ assertSame("replyTextBundle", stubFtpServer.replyTextBundle, commandHandler.getReplyTextBundle());
+ }
+
+ /**
+ * Test the setCommandHandler() method, when the commandName is null
+ */
+ public void testSetCommandHandler_NullCommandName() {
+ CommandHandler commandHandler = (CommandHandler) createMock(CommandHandler.class);
+ try {
+ stubFtpServer.setCommandHandler(null, commandHandler);
+ fail("Expected AssertFailedException");
+ }
+ catch (AssertFailedException expected) {
+ LOG.info("Expected: " + expected);
+ }
+ }
+
+ /**
+ * Test the setCommandHandler() method, when the commandHandler is null
+ */
+ public void testSetCommandHandler_NullCommandHandler() {
+ try {
+ stubFtpServer.setCommandHandler("ZZZ", null);
+ fail("Expected AssertFailedException");
+ }
+ catch (AssertFailedException expected) {
+ LOG.info("Expected: " + expected);
+ }
+ }
+
+ /**
+ * Test setReplyTextBaseName() method
+ */
+ public void testSetReplyTextBaseName() {
+ stubFtpServer.setReplyTextBaseName("SampleReplyText");
+ CwdCommandHandler commandHandler = new CwdCommandHandler();
+
+ // The resource bundle is passed along to new CommandHandlers (if they don't already have one)
+ stubFtpServer.setCommandHandler("CWD", commandHandler);
+ ResourceBundle resourceBundle = commandHandler.getReplyTextBundle();
+ assertEquals("110", "Testing123", resourceBundle.getString("110"));
+ }
+
+ /**
+ * Test the setCommandHandler() and getCommandHandler() methods for commands in lower case or mixed case
+ */
+ public void testLowerCaseOrMixedCaseCommandNames() {
+ stubFtpServer.setCommandHandler("XXX", commandHandler);
+ assertSame("ZZZ", commandHandler, stubFtpServer.getCommandHandler("XXX"));
+ assertSame("Zzz", commandHandler, stubFtpServer.getCommandHandler("Xxx"));
+ assertSame("zzz", commandHandler, stubFtpServer.getCommandHandler("xxx"));
+
+ stubFtpServer.setCommandHandler("YyY", commandHandler);
+ assertSame("ZZZ", commandHandler, stubFtpServer.getCommandHandler("YYY"));
+ assertSame("Zzz", commandHandler, stubFtpServer.getCommandHandler("Yyy"));
+ assertSame("zzz", commandHandler, stubFtpServer.getCommandHandler("yyy"));
+
+ stubFtpServer.setCommandHandler("zzz", commandHandler);
+ assertSame("ZZZ", commandHandler, stubFtpServer.getCommandHandler("ZZZ"));
+ assertSame("Zzz", commandHandler, stubFtpServer.getCommandHandler("zzZ"));
+ assertSame("zzz", commandHandler, stubFtpServer.getCommandHandler("zzz"));
+ }
+
+ //-------------------------------------------------------------------------
+ // Test setup
+ //-------------------------------------------------------------------------
+
+ /**
+ * @see org.mockftpserver.test.AbstractTest#setUp()
+ */
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ stubFtpServer = new StubFtpServer();
+
+ // Create a CommandHandler instance that also implements ResourceBundleAware
+ commandHandler = new AbstractStubCommandHandler() {
+ protected void handleCommand(Command command, Session session, InvocationRecord invocationRecord) throws Exception {
+ }
+ };
+
+ // Create a CommandHandler instance that does NOT implement ResourceBundleAware
+ commandHandler_NoReplyTextBundle = new CommandHandler() {
+ public void handleCommand(Command command, Session session) throws Exception {
+ }
+ };
+ }
+
+}
diff --git a/tags/1.2.3/src/test/java/org/mockftpserver/stub/StubFtpServer_MultipleClientsIntegrationTest.java b/tags/1.2.3/src/test/java/org/mockftpserver/stub/StubFtpServer_MultipleClientsIntegrationTest.java
new file mode 100644
index 0000000..88fab44
--- /dev/null
+++ b/tags/1.2.3/src/test/java/org/mockftpserver/stub/StubFtpServer_MultipleClientsIntegrationTest.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.stub;
+
+import org.apache.commons.net.ftp.FTPClient;
+import org.apache.log4j.Logger;
+import org.mockftpserver.core.command.Command;
+import org.mockftpserver.core.command.CommandNames;
+import org.mockftpserver.core.command.InvocationRecord;
+import org.mockftpserver.core.session.Session;
+import org.mockftpserver.stub.StubFtpServer;
+import org.mockftpserver.stub.command.AbstractStubCommandHandler;
+import org.mockftpserver.test.AbstractTest;
+import org.mockftpserver.test.IntegrationTest;
+import org.mockftpserver.test.PortTestUtil;
+
+/**
+ * StubFtpServer tests for multiple FTP clients using the Apache Jakarta Commons Net FTP client.
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public final class StubFtpServer_MultipleClientsIntegrationTest extends AbstractTest implements
+ IntegrationTest {
+
+ private static final Logger LOG = Logger.getLogger(StubFtpServer_MultipleClientsIntegrationTest.class);
+ private static final String SERVER = "localhost";
+
+ // Custom CommandHandler for PWD so that we can verify unique session-specific responses.
+ // Send back the hashCode for the Session as the reply text.
+ private static class CustomPwdCommandHandler extends AbstractStubCommandHandler {
+ protected void handleCommand(Command command, Session session, InvocationRecord invocationRecord) throws Exception {
+ String replyText = quotes(Integer.toString(session.hashCode()));
+ sendReply(session, 257, null, replyText, null);
+ }
+ }
+
+ private StubFtpServer stubFtpServer;
+ private FTPClient ftpClient1;
+ private FTPClient ftpClient2;
+ private FTPClient ftpClient3;
+
+ /**
+ * Test that multiple simultaneous clients can connect and establish sessions.
+ */
+ public void testMultipleClients() throws Exception {
+
+ // Connect from client 1
+ LOG.info("connect() to ftpClient1");
+ ftpClient1.connect(SERVER, PortTestUtil.getFtpServerControlPort());
+ String sessionId1 = ftpClient1.printWorkingDirectory();
+ LOG.info("PWD(1) reply =[" + sessionId1 + "]");
+
+ // Connect from client 2
+ LOG.info("connect() to ftpClient2");
+ ftpClient2.connect(SERVER, PortTestUtil.getFtpServerControlPort());
+ String sessionId2 = ftpClient2.printWorkingDirectory();
+ LOG.info("PWD(2) reply =[" + sessionId2 + "]");
+
+ // Connect from client 3
+ LOG.info("connect() to ftpClient3");
+ ftpClient3.connect(SERVER, PortTestUtil.getFtpServerControlPort());
+ String sessionId3 = ftpClient3.printWorkingDirectory();
+ LOG.info("PWD(3) reply =[" + sessionId3 + "]");
+
+ // Make sure all session ids are unique
+ assertNotSame("sessionId1 vs sessionId2", sessionId1, sessionId2);
+ assertNotSame("sessionId2 vs sessionId3", sessionId2, sessionId3);
+ assertNotSame("sessionId1 vs sessionId3", sessionId1, sessionId3);
+
+ // Now make sure that the replies from the existing sessions remain consistent
+ assertEquals("reply from session1", sessionId1, ftpClient1.printWorkingDirectory());
+ assertEquals("reply from session2", sessionId2, ftpClient2.printWorkingDirectory());
+ assertEquals("reply from session3", sessionId3, ftpClient3.printWorkingDirectory());
+ }
+
+ // -------------------------------------------------------------------------
+ // Test setup and tear-down
+ // -------------------------------------------------------------------------
+
+ /**
+ * Perform initialization before each test
+ * @see org.mockftpserver.test.AbstractTest#setUp()
+ */
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ stubFtpServer = new StubFtpServer();
+ stubFtpServer.setServerControlPort(PortTestUtil.getFtpServerControlPort());
+ stubFtpServer.setCommandHandler(CommandNames.PWD, new CustomPwdCommandHandler());
+ stubFtpServer.start();
+
+ ftpClient1 = new FTPClient();
+ ftpClient2 = new FTPClient();
+ ftpClient3 = new FTPClient();
+
+ ftpClient1.setDefaultTimeout(1000);
+ ftpClient2.setDefaultTimeout(1000);
+ ftpClient3.setDefaultTimeout(1000);
+ }
+
+ /**
+ * Perform cleanup after each test
+ * @see org.mockftpserver.test.AbstractTest#tearDown()
+ */
+ protected void tearDown() throws Exception {
+ super.tearDown();
+
+ LOG.info("Cleaning up...");
+ if (ftpClient1.isConnected()) {
+ ftpClient1.disconnect();
+ }
+ if (ftpClient2.isConnected()) {
+ ftpClient2.disconnect();
+ }
+ if (ftpClient3.isConnected()) {
+ ftpClient3.disconnect();
+ }
+
+ stubFtpServer.stop();
+ }
+
+}
diff --git a/tags/1.2.3/src/test/java/org/mockftpserver/stub/StubFtpServer_StartTest.java b/tags/1.2.3/src/test/java/org/mockftpserver/stub/StubFtpServer_StartTest.java
new file mode 100644
index 0000000..1f32bda
--- /dev/null
+++ b/tags/1.2.3/src/test/java/org/mockftpserver/stub/StubFtpServer_StartTest.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.stub;
+
+import org.apache.commons.net.ftp.FTPClient;
+import org.mockftpserver.core.command.CommandNames;
+import org.mockftpserver.stub.StubFtpServer;
+import org.mockftpserver.stub.command.PwdCommandHandler;
+import org.mockftpserver.test.AbstractTest;
+import org.mockftpserver.test.PortTestUtil;
+
+/**
+ * Tests for StubFtpServer that require the StubFtpServer thread to be started.
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public final class StubFtpServer_StartTest extends AbstractTest {
+
+ private static final String SERVER = "localhost";
+
+ private StubFtpServer stubFtpServer;
+
+ /**
+ * Test the start() and stop() methods. Start the StubFtpServer and then stop it immediately.
+ */
+ public void testStartAndStop() throws Exception {
+ stubFtpServer = new StubFtpServer();
+ stubFtpServer.setServerControlPort(PortTestUtil.getFtpServerControlPort());
+ assertEquals("started - before", false, stubFtpServer.isStarted());
+
+ stubFtpServer.start();
+ Thread.sleep(200L); // give it some time to get started
+ assertEquals("started - after start()", true, stubFtpServer.isStarted());
+ assertEquals("shutdown - after start()", false, stubFtpServer.isShutdown());
+
+ stubFtpServer.stop();
+
+ assertEquals("shutdown - after stop()", true, stubFtpServer.isShutdown());
+ }
+
+ /**
+ * Test setting a non-default port number for the StubFtpServer control connection socket.
+ */
+ public void testCustomServerControlPort() throws Exception {
+ final int SERVER_CONTROL_PORT = 9187;
+ final String DIR = "abc 1234567";
+ PwdCommandHandler pwd = new PwdCommandHandler();
+ pwd.setDirectory(DIR);
+
+ stubFtpServer = new StubFtpServer();
+ stubFtpServer.setServerControlPort(SERVER_CONTROL_PORT);
+ stubFtpServer.setCommandHandler(CommandNames.PWD, pwd);
+
+ stubFtpServer.start();
+
+ try {
+ FTPClient ftpClient = new FTPClient();
+ ftpClient.connect(SERVER, SERVER_CONTROL_PORT);
+
+ assertEquals("pwd", DIR, ftpClient.printWorkingDirectory());
+ }
+ finally {
+ stubFtpServer.stop();
+ }
+ }
+
+}
diff --git a/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/AborCommandHandlerTest.java b/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/AborCommandHandlerTest.java
new file mode 100644
index 0000000..858195f
--- /dev/null
+++ b/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/AborCommandHandlerTest.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.stub.command;
+
+import org.mockftpserver.core.command.Command;
+import org.mockftpserver.core.command.CommandNames;
+import org.mockftpserver.core.command.ReplyCodes;
+
+/**
+ * Tests for the AborCommandHandler class
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public final class AborCommandHandlerTest extends AbstractCommandHandlerTest {
+
+ private AborCommandHandler commandHandler;
+
+ /**
+ * Test the handleCommand() method
+ */
+ public void testHandleCommand() throws Exception {
+ final Command COMMAND = new Command(CommandNames.ABOR, EMPTY);
+
+ session.sendReply(ReplyCodes.ABOR_OK, replyTextFor(ReplyCodes.ABOR_OK));
+ replay(session);
+
+ commandHandler.handleCommand(COMMAND, session);
+ verify(session);
+
+ verifyNumberOfInvocations(commandHandler, 1);
+ verifyNoDataElements(commandHandler.getInvocation(0));
+ }
+
+ /**
+ * Perform initialization before each test
+ *
+ * @see org.mockftpserver.stub.command.AbstractCommandHandlerTest#setUp()
+ */
+ protected void setUp() throws Exception {
+ super.setUp();
+ commandHandler = new AborCommandHandler();
+ commandHandler.setReplyTextBundle(replyTextBundle);
+ }
+
+}
diff --git a/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/AbstractCommandHandlerTest.java b/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/AbstractCommandHandlerTest.java
new file mode 100644
index 0000000..8dc3999
--- /dev/null
+++ b/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/AbstractCommandHandlerTest.java
@@ -0,0 +1,231 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.stub.command;
+
+import java.text.MessageFormat;
+import java.util.ListResourceBundle;
+import java.util.ResourceBundle;
+
+import org.apache.log4j.Logger;
+import org.easymock.MockControl;
+import org.mockftpserver.core.command.AbstractCommandHandler;
+import org.mockftpserver.core.command.Command;
+import org.mockftpserver.core.command.InvocationHistory;
+import org.mockftpserver.core.command.InvocationRecord;
+import org.mockftpserver.core.session.Session;
+import org.mockftpserver.core.util.AssertFailedException;
+import org.mockftpserver.test.AbstractTest;
+
+/**
+ * Abstract superclass for CommandHandler tests
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public abstract class AbstractCommandHandlerTest extends AbstractTest {
+
+ private static final Logger LOG = Logger.getLogger(AbstractCommandHandlerTest.class);
+
+ // Some common test constants
+ protected static final String DIR1 = "dir1";
+ protected static final String DIR2 = "dir2";
+ protected static final String FILENAME1 = "sample1.txt";
+ protected static final String FILENAME2 = "sample2.txt";
+
+ protected Session session;
+ protected ResourceBundle replyTextBundle;
+
+ /**
+ * Test the handleCommand() method, when one or more parameter is missing or invalid
+ *
+ * @param commandHandler - the CommandHandler to test
+ * @param commandName - the name for the Command
+ * @param parameters - the Command parameters
+ */
+ protected void testHandleCommand_InvalidParameters(AbstractCommandHandler commandHandler,
+ String commandName, String[] parameters) throws Exception {
+
+ try {
+ commandHandler.handleCommand(new Command(commandName, parameters), session);
+ fail("Expected AssertFailedException");
+ }
+ catch (AssertFailedException expected) {
+ LOG.info("Expected: " + expected);
+ assertTrue("Error message must contain [" + commandName + "]", expected.getMessage().indexOf(
+ commandName) != -1);
+ }
+ verifyNumberOfInvocations(commandHandler, 1);
+ }
+
+ /**
+ * Verify that the CommandHandler contains the specified number of invocation records
+ *
+ * @param commandHandler - the CommandHandler
+ * @param expected - the expected number of invocations
+ */
+ protected void verifyNumberOfInvocations(InvocationHistory commandHandler, int expected) {
+ assertEquals("number of invocations", expected, commandHandler.numberOfInvocations());
+ }
+
+ /**
+ * Verify that the InvocationRecord contains no data elements
+ *
+ * @param invocationRecord - the InvocationRecord
+ */
+ protected void verifyNoDataElements(InvocationRecord invocationRecord) {
+ LOG.info("Verifying: " + invocationRecord);
+ assertEquals("number of data elements", 0, invocationRecord.keySet().size());
+ }
+
+ /**
+ * Verify that the InvocationRecord contains exactly one data element, with the specified key
+ * and value.
+ *
+ * @param invocationRecord - the InvocationRecord
+ * @param key - the expected key
+ * @param value - the expected value
+ */
+ protected void verifyOneDataElement(InvocationRecord invocationRecord, String key, Object value) {
+ LOG.info("Verifying: " + invocationRecord);
+ assertEquals("number of data elements", 1, invocationRecord.keySet().size());
+ assertEqualsAllTypes("value:" + value, value, invocationRecord.getObject(key));
+ }
+
+ /**
+ * Verify that the InvocationRecord contains exactly two data element, with the specified keys
+ * and values.
+ *
+ * @param invocationRecord - the InvocationRecord
+ * @param key1 - the expected key1
+ * @param value1 - the expected value1
+ * @param key2 - the expected key2
+ * @param value12- the expected value2
+ */
+ protected void verifyTwoDataElements(InvocationRecord invocationRecord, String key1, Object value1,
+ String key2, Object value2) {
+
+ LOG.info("Verifying: " + invocationRecord);
+ assertEquals("number of data elements", 2, invocationRecord.keySet().size());
+ assertEqualsAllTypes("value1:" + value1, value1, invocationRecord.getObject(key1));
+ assertEqualsAllTypes("value2:" + value2, value2, invocationRecord.getObject(key2));
+ }
+
+ /**
+ * Assert that the actual is equal to the expected, using arrays equality comparison if
+ * necessary
+ *
+ * @param message - the message, used if the comparison fails
+ * @param expected - the expected value
+ * @param actual - the actual value
+ */
+ private void assertEqualsAllTypes(String message, Object expected, Object actual) {
+
+ if (expected instanceof byte[] || actual instanceof byte[]) {
+ assertEquals(message, (byte[]) expected, (byte[]) actual);
+ }
+ else if (expected instanceof Object[] || actual instanceof Object[]) {
+ assertEquals(message, (Object[]) expected, (Object[]) actual);
+ }
+ else {
+ assertEquals(message, expected, actual);
+ }
+ }
+
+ /**
+ * Perform setup before each test
+ *
+ * @see org.mockftpserver.test.AbstractTest#setUp()
+ */
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ session = (Session) createMock(Session.class);
+ control(session).setDefaultMatcher(MockControl.ARRAY_MATCHER);
+ control(session).expectAndDefaultReturn(session.getClientHost(), DEFAULT_HOST);
+
+ replyTextBundle = new ListResourceBundle() {
+ protected Object[][] getContents() {
+ return new Object[][] {
+ { "150", replyTextFor(150) },
+ { "200", replyTextFor(200) },
+ { "211", replyTextWithParameterFor(211) },
+ { "213", replyTextWithParameterFor(213) },
+ { "214", replyTextWithParameterFor(214) },
+ { "215", replyTextWithParameterFor(215) },
+ { "220", replyTextFor(220) },
+ { "221", replyTextFor(221) },
+ { "226", replyTextFor(226) },
+ { "226.WithFilename", replyTextWithParameterFor("226.WithFilename") },
+ { "227", replyTextWithParameterFor(227) },
+ { "230", replyTextFor(230) },
+ { "250", replyTextFor(250) },
+ { "257", replyTextWithParameterFor(257) },
+ { "331", replyTextFor(331) },
+ { "350", replyTextFor(350) },
+ };
+ }
+ };
+ }
+
+ /**
+ * Return the test-specific reply text for the specified reply code
+ * @param replyCode - the reply code
+ * @return the reply text for the specified reply code
+ */
+ protected String replyTextFor(int replyCode) {
+ return "Reply for " + replyCode;
+ }
+
+ /**
+ * Return the test-specific parameterized reply text for the specified reply code
+ * @param replyCode - the reply code
+ * @return the reply text for the specified reply code
+ */
+ protected String replyTextWithParameterFor(int replyCode) {
+ return "Reply for " + replyCode + ":{0}";
+ }
+
+ /**
+ * Return the test-specific parameterized reply text for the specified messageKey
+ * @param messageKey - the messageKey
+ * @return the reply text for the specified messageKey
+ */
+ protected String replyTextWithParameterFor(String messageKey) {
+ return "Reply for " + messageKey + ":{0}";
+ }
+
+ /**
+ * Return the test-specific reply text for the specified reply code and message parameter
+ * @param replyCode - the reply code
+ * @param parameter - the message parameter value
+ * @return the reply text for the specified reply code
+ */
+ protected String formattedReplyTextFor(int replyCode, Object parameter) {
+ return MessageFormat.format(replyTextWithParameterFor(replyCode), objArray(parameter));
+ }
+
+ /**
+ * Return the test-specific reply text for the specified message key and message parameter
+ * @param messageKey - the messageKey
+ * @param parameter - the message parameter value
+ * @return the reply text for the specified message key and parameter
+ */
+ protected String formattedReplyTextFor(String messageKey, Object parameter) {
+ return MessageFormat.format(replyTextWithParameterFor(messageKey), objArray(parameter));
+ }
+
+}
diff --git a/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/AcctCommandHandlerTest.java b/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/AcctCommandHandlerTest.java
new file mode 100644
index 0000000..c68ddc9
--- /dev/null
+++ b/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/AcctCommandHandlerTest.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.stub.command;
+
+import org.mockftpserver.core.command.Command;
+import org.mockftpserver.core.command.CommandNames;
+import org.mockftpserver.core.command.ReplyCodes;
+
+/**
+ * Tests for the AcctCommandHandler class
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public final class AcctCommandHandlerTest extends AbstractCommandHandlerTest {
+
+ private static final String ACCOUNT1 = "account1";
+ private static final String ACCOUNT2 = "account2";
+
+ private AcctCommandHandler commandHandler;
+ private Command command1;
+ private Command command2;
+
+ /**
+ * Test the handleCommand() method
+ * @throws Exception
+ */
+ public void testHandleCommand() throws Exception {
+
+ session.sendReply(ReplyCodes.ACCT_OK, replyTextFor(ReplyCodes.ACCT_OK));
+ session.sendReply(ReplyCodes.ACCT_OK, replyTextFor(ReplyCodes.ACCT_OK));
+ replay(session);
+
+ commandHandler.handleCommand(command1, session);
+ commandHandler.handleCommand(command2, session);
+ verify(session);
+
+ verifyNumberOfInvocations(commandHandler, 2);
+ verifyOneDataElement(commandHandler.getInvocation(0), AcctCommandHandler.ACCOUNT_KEY, ACCOUNT1);
+ verifyOneDataElement(commandHandler.getInvocation(1), AcctCommandHandler.ACCOUNT_KEY, ACCOUNT2);
+ }
+
+ /**
+ * Test the handleCommand() method, when no password parameter has been specified
+ */
+ public void testHandleCommand_MissingPasswordParameter() throws Exception {
+ testHandleCommand_InvalidParameters(commandHandler, CommandNames.ACCT, EMPTY);
+ }
+
+ /**
+ * Perform initialization before each test
+ * @see org.mockftpserver.stub.command.AbstractCommandHandlerTest#setUp()
+ */
+ protected void setUp() throws Exception {
+ super.setUp();
+ commandHandler = new AcctCommandHandler();
+ commandHandler.setReplyTextBundle(replyTextBundle);
+ command1 = new Command(CommandNames.ACCT, array(ACCOUNT1));
+ command2 = new Command(CommandNames.ACCT, array(ACCOUNT2));
+ }
+
+}
diff --git a/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/AlloCommandHandlerTest.java b/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/AlloCommandHandlerTest.java
new file mode 100644
index 0000000..e5d04a6
--- /dev/null
+++ b/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/AlloCommandHandlerTest.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.stub.command;
+
+import org.apache.log4j.Logger;
+import org.mockftpserver.core.command.Command;
+import org.mockftpserver.core.command.CommandNames;
+import org.mockftpserver.core.command.ReplyCodes;
+import org.mockftpserver.core.util.AssertFailedException;
+
+/**
+ * Tests for the AlloCommandHandler class
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public final class AlloCommandHandlerTest extends AbstractCommandHandlerTest {
+
+ private static final Logger LOG = Logger.getLogger(AlloCommandHandlerTest.class);
+ private static final int BYTES1 = 64;
+ private static final int BYTES2 = 555;
+ private static final int RECORD_SIZE = 77;
+
+ private AlloCommandHandler commandHandler;
+ private Command command1;
+ private Command command2;
+
+ /**
+ * Test the handleCommand() method
+ */
+ public void testHandleCommand() throws Exception {
+
+ session.sendReply(ReplyCodes.ALLO_OK, replyTextFor(ReplyCodes.ALLO_OK));
+ session.sendReply(ReplyCodes.ALLO_OK, replyTextFor(ReplyCodes.ALLO_OK));
+ replay(session);
+
+ commandHandler.handleCommand(command1, session);
+ commandHandler.handleCommand(command2, session);
+ verify(session);
+
+ verifyNumberOfInvocations(commandHandler, 2);
+ verifyOneDataElement(commandHandler.getInvocation(0), AlloCommandHandler.NUMBER_OF_BYTES_KEY, new Integer(
+ BYTES1));
+ verifyTwoDataElements(commandHandler.getInvocation(1), AlloCommandHandler.NUMBER_OF_BYTES_KEY, new Integer(
+ BYTES2), AlloCommandHandler.RECORD_SIZE_KEY, new Integer(RECORD_SIZE));
+ }
+
+ /**
+ * Test the handleCommand() method, when no numberOfBytes parameter has been specified
+ */
+ public void testHandleCommand_MissingNumberOfBytesParameter() throws Exception {
+ testHandleCommand_InvalidParameters(commandHandler, CommandNames.ALLO, EMPTY);
+ }
+
+ /**
+ * Test the handleCommand() method, when the recordSize delimiter ("R") parameter is specified,
+ * but is not followed by the recordSize value.
+ */
+ public void testHandleCommand_RecordSizeDelimiterWithoutValue() throws Exception {
+ try {
+ commandHandler.handleCommand(new Command(CommandNames.ALLO, array("123 R ")), session);
+ fail("Expected AssertFailedException");
+ }
+ catch (AssertFailedException expected) {
+ LOG.info("Expected: " + expected);
+ }
+ }
+
+ /**
+ * Test the handleCommand() method, when a non-numeric numberOfBytes parameter has been
+ * specified
+ */
+ public void testHandleCommand_InvalidNumberOfBytesParameter() throws Exception {
+ try {
+ commandHandler.handleCommand(new Command(CommandNames.ALLO, array("xx")), session);
+ fail("Expected NumberFormatException");
+ }
+ catch (NumberFormatException expected) {
+ LOG.info("Expected: " + expected);
+ }
+ }
+
+ /**
+ * Test the handleCommand() method, when a non-numeric recordSize parameter has been specified
+ */
+ public void testHandleCommand_InvalidRecordSizeParameter() throws Exception {
+ try {
+ commandHandler.handleCommand(new Command(CommandNames.ALLO, array("123 R xx")), session);
+ fail("Expected NumberFormatException");
+ }
+ catch (NumberFormatException expected) {
+ LOG.info("Expected: " + expected);
+ }
+ }
+
+ /**
+ * Perform initialization before each test
+ *
+ * @see org.mockftpserver.stub.command.AbstractCommandHandlerTest#setUp()
+ */
+ protected void setUp() throws Exception {
+ super.setUp();
+ commandHandler = new AlloCommandHandler();
+ commandHandler.setReplyTextBundle(replyTextBundle);
+ command1 = new Command(CommandNames.ALLO, array(Integer.toString(BYTES1)));
+ command2 = new Command(CommandNames.ALLO, array(Integer.toString(BYTES2) + " R " + Integer.toString(RECORD_SIZE)));
+ }
+
+}
diff --git a/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/AppeCommandHandlerTest.java b/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/AppeCommandHandlerTest.java
new file mode 100644
index 0000000..c3d41d5
--- /dev/null
+++ b/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/AppeCommandHandlerTest.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.stub.command;
+
+import org.mockftpserver.core.command.Command;
+import org.mockftpserver.core.command.CommandNames;
+import org.mockftpserver.core.command.ReplyCodes;
+import org.mockftpserver.stub.command.AppeCommandHandler;
+
+/**
+ * Tests for the AppeCommandHandler class
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public final class AppeCommandHandlerTest extends AbstractCommandHandlerTest {
+
+ private AppeCommandHandler commandHandler;
+
+ /**
+ * Perform initialization before each test
+ *
+ * @see org.mockftpserver.stub.command.AbstractCommandHandlerTest#setUp()
+ */
+ protected void setUp() throws Exception {
+ super.setUp();
+ commandHandler = new AppeCommandHandler();
+ commandHandler.setReplyTextBundle(replyTextBundle);
+ }
+
+ /**
+ * Test the handleCommand() method, as well as the getFileContents() and clearFileContents() methods
+ */
+ public void testHandleCommand() throws Exception {
+ final String DATA = "ABC";
+
+ session.sendReply(ReplyCodes.SEND_DATA_INITIAL_OK, replyTextFor(ReplyCodes.SEND_DATA_INITIAL_OK));
+ session.openDataConnection();
+ session.readData();
+ control(session).setReturnValue(DATA.getBytes());
+ session.closeDataConnection();
+ session.sendReply(ReplyCodes.SEND_DATA_FINAL_OK, replyTextFor(ReplyCodes.SEND_DATA_FINAL_OK));
+ replay(session);
+
+ Command command = new Command(CommandNames.APPE, array(FILENAME1));
+ commandHandler.handleCommand(command, session);
+ verify(session);
+
+ verifyNumberOfInvocations(commandHandler, 1);
+ verifyTwoDataElements(commandHandler.getInvocation(0), AppeCommandHandler.PATHNAME_KEY, FILENAME1,
+ AppeCommandHandler.FILE_CONTENTS_KEY, DATA.getBytes());
+ }
+
+ /**
+ * Test the handleCommand() method, when no pathname parameter has been specified
+ */
+ public void testHandleCommand_MissingPathnameParameter() throws Exception {
+ testHandleCommand_InvalidParameters(commandHandler, CommandNames.APPE, EMPTY);
+ }
+
+}
diff --git a/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/CdupCommandHandlerTest.java b/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/CdupCommandHandlerTest.java
new file mode 100644
index 0000000..582e102
--- /dev/null
+++ b/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/CdupCommandHandlerTest.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.stub.command;
+
+import org.mockftpserver.core.command.Command;
+import org.mockftpserver.core.command.CommandNames;
+import org.mockftpserver.core.command.ReplyCodes;
+import org.mockftpserver.stub.command.CdupCommandHandler;
+
+/**
+ * Tests for the CdupCommandHandler class
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public final class CdupCommandHandlerTest extends AbstractCommandHandlerTest {
+
+ private CdupCommandHandler commandHandler;
+ private Command command1;
+ private Command command2;
+
+ /**
+ * Test the handleCommand(Command,Session) method
+ * @throws Exception
+ */
+ public void testHandleCommand() throws Exception {
+ session.sendReply(ReplyCodes.CDUP_OK, replyTextFor(ReplyCodes.CDUP_OK));
+ session.sendReply(ReplyCodes.CDUP_OK, replyTextFor(ReplyCodes.CDUP_OK));
+ replay(session);
+
+ commandHandler.handleCommand(command1, session);
+ commandHandler.handleCommand(command2, session);
+ verify(session);
+
+ verifyNumberOfInvocations(commandHandler, 2);
+ verifyNoDataElements(commandHandler.getInvocation(0));
+ verifyNoDataElements(commandHandler.getInvocation(1));
+ }
+
+ /**
+ * Perform initialization before each test
+ * @see org.mockftpserver.stub.command.AbstractCommandHandlerTest#setUp()
+ */
+ protected void setUp() throws Exception {
+ super.setUp();
+ commandHandler = new CdupCommandHandler();
+ commandHandler.setReplyTextBundle(replyTextBundle);
+ command1 = new Command(CommandNames.CDUP, EMPTY);
+ command2 = new Command(CommandNames.CDUP, EMPTY);
+ }
+}
diff --git a/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/CommandTest.java b/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/CommandTest.java
new file mode 100644
index 0000000..d104885
--- /dev/null
+++ b/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/CommandTest.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.stub.command;
+
+import org.apache.log4j.Logger;
+import org.mockftpserver.core.command.Command;
+import org.mockftpserver.core.util.AssertFailedException;
+import org.mockftpserver.test.AbstractTest;
+
+/**
+ * Tests for the Command class
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public final class CommandTest extends AbstractTest {
+
+ private static final Logger LOG = Logger.getLogger(CommandTest.class);
+
+ /**
+ * Test the constructor
+ */
+ public void testConstructor() {
+ final String[] PARAMETERS = array("123");
+ Command command = new Command("abc", PARAMETERS);
+ assertEquals("name", "abc", command.getName());
+ assertEquals("parameters", PARAMETERS, command.getParameters());
+ }
+
+ /**
+ * Test the Constructor method, passing in a null name
+ */
+ public void testConstructor_NullName() {
+ try {
+ new Command(null, EMPTY);
+ fail("Expected AssertFailedException");
+ }
+ catch (AssertFailedException expected) {
+ LOG.info("Expected: " + expected);
+ }
+ }
+
+ /**
+ * Test the Constructor method, passing in a null parameters
+ */
+ public void testConstructor_NullParameters() {
+ try {
+ new Command("OK", null);
+ fail("Expected AssertFailedException");
+ }
+ catch (AssertFailedException expected) {
+ LOG.info("Expected: " + expected);
+ }
+ }
+
+ /**
+ * Test the normalizeName() method
+ */
+ public void testNormalizeName() {
+ assertEquals("XXX", "XXX", Command.normalizeName("XXX"));
+ assertEquals("xxx", "XXX", Command.normalizeName("xxx"));
+ assertEquals("Xxx", "XXX", Command.normalizeName("Xxx"));
+ }
+
+ /**
+ * Test the getRequiredString method
+ */
+ public void testGetRequiredString() {
+ Command command = new Command("abc", array("123", "456"));
+ assertEquals("123", "123", command.getRequiredString(0));
+ assertEquals("456", "456", command.getRequiredString(1));
+ }
+
+ /**
+ * Test the getRequiredString method, when the index is not valid
+ */
+ public void testGetRequiredString_IndexNotValid() {
+ Command command = new Command("abc", array("123", "456"));
+ try {
+ command.getRequiredString(2);
+ fail("Expected AssertFailedException");
+ }
+ catch (AssertFailedException expected) {
+ LOG.info("Expected: " + expected);
+ }
+ }
+
+ /**
+ * Test the getOptionalString method
+ */
+ public void testGetOptionalString() {
+ Command command = new Command("abc", array("123", "456"));
+ assertEquals("123", "123", command.getOptionalString(0));
+ assertEquals("456", "456", command.getOptionalString(1));
+ assertEquals("null", null, command.getOptionalString(2));
+ }
+
+ /**
+ * Test that a Command object is immutable, changing the original parameters passed in to the constructor
+ */
+ public void testImmutable_ChangeOriginalParameters() {
+ final String[] PARAMETERS = { "a", "b", "c" };
+ final Command COMMAND = new Command("command", PARAMETERS);
+ PARAMETERS[2] = "xxx";
+ assertEquals("parameters", COMMAND.getParameters(), new String[] { "a", "b", "c" });
+ }
+
+ /**
+ * Test that a Command object is immutable, changing the parameters returned from getParameters
+ */
+ public void testImmutable_ChangeRetrievedParameters() {
+ final String[] PARAMETERS = { "a", "b", "c" };
+ final Command COMMAND = new Command("command", PARAMETERS);
+ String[] parameters = COMMAND.getParameters();
+ parameters[2] = "xxx";
+ assertEquals("parameters", PARAMETERS, COMMAND.getParameters());
+ }
+
+ /**
+ * Test the equals() method, and tests the hasCode() method implicitly
+ * @throws Exception
+ */
+ public void testEquals() throws Exception {
+ final Command COMMAND1 = new Command("a", EMPTY);
+ final Command COMMAND2 = new Command("a", EMPTY);
+ final Command COMMAND3 = new Command("b", array("1"));
+ final Command COMMAND4 = new Command("b", array("2"));
+ final Command COMMAND5 = new Command("c", array("1"));
+ _testEquals(COMMAND1, null, false);
+ _testEquals(COMMAND1, COMMAND1, true);
+ _testEquals(COMMAND1, COMMAND2, true);
+ _testEquals(COMMAND1, COMMAND3, false);
+ _testEquals(COMMAND3, COMMAND4, false);
+ _testEquals(COMMAND3, COMMAND5, false);
+ }
+
+ /**
+ * Test that command1 equals command2 if and only if expectedEqual is true
+ * @param command1 - the first command
+ * @param command2 - the second command
+ * @param expectedEqual - true if command1 is expected to equal command2
+ */
+ private void _testEquals(Command command1, Command command2, boolean expectedEqual) {
+ assertEquals(command1.toString() + " and " + command2, expectedEqual, command1.equals(command2));
+ }
+
+}
diff --git a/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/ConnectCommandHandlerTest.java b/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/ConnectCommandHandlerTest.java
new file mode 100644
index 0000000..862638c
--- /dev/null
+++ b/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/ConnectCommandHandlerTest.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.stub.command;
+
+import org.mockftpserver.core.command.Command;
+import org.mockftpserver.core.command.CommandNames;
+import org.mockftpserver.core.command.ReplyCodes;
+import org.mockftpserver.stub.command.ConnectCommandHandler;
+
+/**
+ * Tests for the ConnectCommandHandler class
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public final class ConnectCommandHandlerTest extends AbstractCommandHandlerTest {
+
+ private ConnectCommandHandler commandHandler;
+ private Command command1;
+
+ /**
+ * Test the handleCommand() method
+ * @throws Exception
+ */
+ public void testHandleCommand() throws Exception {
+
+ session.sendReply(ReplyCodes.CONNECT_OK, replyTextFor(ReplyCodes.CONNECT_OK));
+ replay(session);
+
+ commandHandler.handleCommand(command1, session);
+ verify(session);
+
+ verifyNumberOfInvocations(commandHandler, 1);
+ verifyNoDataElements(commandHandler.getInvocation(0));
+ }
+
+ /**
+ * Perform initialization before each test
+ * @see org.mockftpserver.stub.command.AbstractCommandHandlerTest#setUp()
+ */
+ protected void setUp() throws Exception {
+ super.setUp();
+ commandHandler = new ConnectCommandHandler();
+ commandHandler.setReplyTextBundle(replyTextBundle);
+ command1 = new Command(CommandNames.CONNECT, EMPTY);
+ }
+
+}
diff --git a/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/CwdCommandHandlerTest.java b/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/CwdCommandHandlerTest.java
new file mode 100644
index 0000000..921217b
--- /dev/null
+++ b/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/CwdCommandHandlerTest.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.stub.command;
+
+import org.mockftpserver.core.command.Command;
+import org.mockftpserver.core.command.CommandNames;
+import org.mockftpserver.core.command.ReplyCodes;
+import org.mockftpserver.stub.command.CwdCommandHandler;
+
+/**
+ * Tests for the CwdCommandHandler class
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public final class CwdCommandHandlerTest extends AbstractCommandHandlerTest {
+
+ private CwdCommandHandler commandHandler;
+ private Command command1;
+ private Command command2;
+
+ /**
+ * Test the handleCommand(Command,Session) method
+ * @throws Exception
+ */
+ public void testHandleCommand() throws Exception {
+ session.sendReply(ReplyCodes.CWD_OK, replyTextFor(ReplyCodes.CWD_OK));
+ session.sendReply(ReplyCodes.CWD_OK, replyTextFor(ReplyCodes.CWD_OK));
+ replay(session);
+
+ commandHandler.handleCommand(command1, session);
+ commandHandler.handleCommand(command2, session);
+ verify(session);
+
+ verifyNumberOfInvocations(commandHandler, 2);
+ verifyOneDataElement(commandHandler.getInvocation(0), CwdCommandHandler.PATHNAME_KEY, DIR1);
+ verifyOneDataElement(commandHandler.getInvocation(1), CwdCommandHandler.PATHNAME_KEY, DIR2);
+ }
+
+ /**
+ * Test the handleCommand() method, when no pathname parameter has been specified
+ */
+ public void testHandleCommand_MissingPathnameParameter() throws Exception {
+ testHandleCommand_InvalidParameters(commandHandler, CommandNames.CWD, EMPTY);
+ }
+
+ /**
+ * Perform initialization before each test
+ * @see org.mockftpserver.stub.command.AbstractCommandHandlerTest#setUp()
+ */
+ protected void setUp() throws Exception {
+ super.setUp();
+ commandHandler = new CwdCommandHandler();
+ commandHandler.setReplyTextBundle(replyTextBundle);
+ command1 = new Command(CommandNames.CWD, array(DIR1));
+ command2 = new Command(CommandNames.CWD, array(DIR2));
+ }
+}
diff --git a/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/DeleCommandHandlerTest.java b/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/DeleCommandHandlerTest.java
new file mode 100644
index 0000000..4f22958
--- /dev/null
+++ b/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/DeleCommandHandlerTest.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.stub.command;
+
+import org.mockftpserver.core.command.Command;
+import org.mockftpserver.core.command.CommandNames;
+import org.mockftpserver.core.command.ReplyCodes;
+import org.mockftpserver.stub.command.DeleCommandHandler;
+
+/**
+ * Tests for the DeleCommandHandler class
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public final class DeleCommandHandlerTest extends AbstractCommandHandlerTest {
+
+ private DeleCommandHandler commandHandler;
+ private Command command1;
+ private Command command2;
+
+ /**
+ * Test the handleCommand(Command,Session) method
+ * @throws Exception
+ */
+ public void testHandleCommand() throws Exception {
+ session.sendReply(ReplyCodes.DELE_OK, replyTextFor(ReplyCodes.DELE_OK));
+ session.sendReply(ReplyCodes.DELE_OK, replyTextFor(ReplyCodes.DELE_OK));
+ replay(session);
+
+ commandHandler.handleCommand(command1, session);
+ commandHandler.handleCommand(command2, session);
+ verify(session);
+
+ verifyNumberOfInvocations(commandHandler, 2);
+ verifyOneDataElement(commandHandler.getInvocation(0), DeleCommandHandler.PATHNAME_KEY, FILENAME1);
+ verifyOneDataElement(commandHandler.getInvocation(1), DeleCommandHandler.PATHNAME_KEY, FILENAME2);
+ }
+
+ /**
+ * Test the handleCommand() method, when no pathname parameter has been specified
+ */
+ public void testHandleCommand_MissingPathnameParameter() throws Exception {
+ testHandleCommand_InvalidParameters(commandHandler, CommandNames.DELE, EMPTY);
+ }
+
+ /**
+ * Perform initialization before each test
+ * @see org.mockftpserver.stub.command.AbstractCommandHandlerTest#setUp()
+ */
+ protected void setUp() throws Exception {
+ super.setUp();
+ commandHandler = new DeleCommandHandler();
+ commandHandler.setReplyTextBundle(replyTextBundle);
+ command1 = new Command(CommandNames.DELE, array(FILENAME1));
+ command2 = new Command(CommandNames.DELE, array(FILENAME2));
+ }
+}
diff --git a/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/FileRetrCommandHandlerTest.java b/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/FileRetrCommandHandlerTest.java
new file mode 100644
index 0000000..2192ff6
--- /dev/null
+++ b/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/FileRetrCommandHandlerTest.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.stub.command;
+
+import java.util.Arrays;
+
+
+import org.apache.log4j.Logger;
+import org.easymock.ArgumentsMatcher;
+import org.mockftpserver.core.command.Command;
+import org.mockftpserver.core.command.CommandNames;
+import org.mockftpserver.core.command.ReplyCodes;
+import org.mockftpserver.core.util.AssertFailedException;
+import org.mockftpserver.stub.command.FileRetrCommandHandler;
+
+/**
+ * Tests for the FileRetrCommandHandler class
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public final class FileRetrCommandHandlerTest extends AbstractCommandHandlerTest {
+
+ private static final Logger LOG = Logger.getLogger(FileRetrCommandHandlerTest.class);
+ private static final byte BYTE1 = (byte)7;
+ private static final byte BYTE2 = (byte)21;
+
+ private FileRetrCommandHandler commandHandler;
+
+ /**
+ * Test the constructor that takes a String, passing in a null
+ */
+ public void testConstructor_String_Null() {
+ try {
+ new FileRetrCommandHandler((String)null);
+ fail("Expected AssertFailedException");
+ }
+ catch (AssertFailedException expected) {
+ LOG.info("Expected: " + expected);
+ }
+ }
+
+ /**
+ * Test the setFile(String) method, passing in a null
+ */
+ public void testSetFile_Null() {
+ try {
+ commandHandler.setFile(null);
+ fail("Expected AssertFailedException");
+ }
+ catch (AssertFailedException expected) {
+ LOG.info("Expected: " + expected);
+ }
+ }
+
+ /**
+ * Test the handleCommand(Command,Session) method. Create a temporary (binary) file, and
+ * make sure its contents are written back
+ * @throws Exception
+ */
+ public void testHandleCommand() throws Exception {
+
+ final byte[] BUFFER = new byte[FileRetrCommandHandler.BUFFER_SIZE];
+ Arrays.fill(BUFFER, BYTE1);
+
+ session.sendReply(ReplyCodes.SEND_DATA_INITIAL_OK, replyTextFor(ReplyCodes.SEND_DATA_INITIAL_OK));
+ session.openDataConnection();
+
+ ArgumentsMatcher matcher = new ArgumentsMatcher() {
+ int counter = -1; // will increment for each invocation
+ public boolean matches(Object[] expected, Object[] actual) {
+ counter++;
+ byte[] buffer = (byte[])actual[0];
+ int expectedLength = ((Integer)expected[1]).intValue();
+ int actualLength = ((Integer)actual[1]).intValue();
+ LOG.info("invocation #" + counter + " expected=" + expectedLength + " actualLength=" + actualLength);
+ if (counter < 5) {
+ assertEquals("buffer for invocation #" + counter, BUFFER, buffer);
+ }
+ else {
+ // TODO Got two invocations here; only expected one
+ //assertEquals("length for invocation #" + counter, expectedLength, actualLength);
+ assertEquals("buffer[0]", BYTE2, buffer[0]);
+ assertEquals("buffer[1]", BYTE2, buffer[1]);
+ assertEquals("buffer[2]", BYTE2, buffer[2]);
+ }
+ return true;
+ }
+ public String toString(Object[] args) {
+ return args[0].getClass().getName() + " " + args[1].toString();
+ }
+ };
+
+ session.sendData(BUFFER, 512);
+ control(session).setMatcher(matcher);
+ session.sendData(BUFFER, 512);
+ session.sendData(BUFFER, 512);
+ session.sendData(BUFFER, 512);
+ session.sendData(BUFFER, 512);
+ session.sendData(BUFFER, 3);
+
+ session.closeDataConnection();
+ session.sendReply(ReplyCodes.SEND_DATA_FINAL_OK, replyTextFor(ReplyCodes.SEND_DATA_FINAL_OK));
+ replay(session);
+
+ commandHandler.setFile("Sample.data");
+ Command command = new Command(CommandNames.RETR, array(FILENAME1));
+ commandHandler.handleCommand(command, session);
+ verify(session);
+
+ verifyNumberOfInvocations(commandHandler, 1);
+ verifyOneDataElement(commandHandler.getInvocation(0), FileRetrCommandHandler.PATHNAME_KEY, FILENAME1);
+ }
+
+ /**
+ * Test the handleCommand() method, when no pathname parameter has been specified
+ */
+ public void testHandleCommand_MissingPathnameParameter() throws Exception {
+ commandHandler.setFile("abc.txt"); // this property must be set
+ testHandleCommand_InvalidParameters(commandHandler, CommandNames.RETR, EMPTY);
+ }
+
+ /**
+ * Test the HandleCommand method, when the file property has not been set
+ */
+ public void testHandleCommand_FileNotSet() throws Exception {
+ try {
+ commandHandler.handleCommand(new Command(CommandNames.RETR, EMPTY), session);
+ fail("Expected AssertFailedException");
+ }
+ catch (AssertFailedException expected) {
+ LOG.info("Expected: " + expected);
+ }
+ }
+
+ /**
+ * Perform initialization before each test
+ * @see org.mockftpserver.stub.command.AbstractCommandHandlerTest#setUp()
+ */
+ protected void setUp() throws Exception {
+ super.setUp();
+ commandHandler = new FileRetrCommandHandler();
+ commandHandler.setReplyTextBundle(replyTextBundle);
+ }
+
+// /**
+// * Create a sample binary file; 5 buffers full plus 3 extra bytes
+// */
+// private void createSampleFile() {
+// final String FILE_PATH = "test/org.mockftpserver/command/Sample.data";
+// final byte[] BUFFER = new byte[FileRetrCommandHandler.BUFFER_SIZE];
+// Arrays.fill(BUFFER, BYTE1);
+//
+// File file = new File(FILE_PATH);
+// FileOutputStream out = new FileOutputStream(file);
+// for (int i = 0; i < 5; i++) {
+// out.write(BUFFER);
+// }
+// Arrays.fill(BUFFER, BYTE2);
+// out.write(BUFFER, 0, 3);
+// out.close();
+// LOG.info("Created temporary file [" + FILE_PATH + "]: length=" + file.length());
+// }
+
+}
diff --git a/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/HelpCommandHandlerTest.java b/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/HelpCommandHandlerTest.java
new file mode 100644
index 0000000..46fff3e
--- /dev/null
+++ b/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/HelpCommandHandlerTest.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.stub.command;
+
+import org.mockftpserver.core.command.Command;
+import org.mockftpserver.core.command.CommandNames;
+import org.mockftpserver.core.command.ReplyCodes;
+import org.mockftpserver.stub.command.HelpCommandHandler;
+
+/**
+ * Tests for the HelpCommandHandler class
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public final class HelpCommandHandlerTest extends AbstractCommandHandlerTest {
+
+ private HelpCommandHandler commandHandler;
+
+ /**
+ * Test the handleCommand() method
+ */
+ public void testHandleCommand() throws Exception {
+
+ final String RESPONSE_DATA = "help for ABC...";
+ commandHandler.setHelpMessage(RESPONSE_DATA);
+
+ session.sendReply(ReplyCodes.HELP_OK, formattedReplyTextFor(ReplyCodes.HELP_OK, RESPONSE_DATA));
+ session.sendReply(ReplyCodes.HELP_OK, formattedReplyTextFor(ReplyCodes.HELP_OK, RESPONSE_DATA));
+ replay(session);
+
+ final Command COMMAND1 = new Command(CommandNames.HELP, EMPTY);
+ final Command COMMAND2 = new Command(CommandNames.HELP, array("abc"));
+
+ commandHandler.handleCommand(COMMAND1, session);
+ commandHandler.handleCommand(COMMAND2, session);
+ verify(session);
+
+ verifyNumberOfInvocations(commandHandler, 2);
+ verifyOneDataElement(commandHandler.getInvocation(0), HelpCommandHandler.COMMAND_NAME_KEY, null);
+ verifyOneDataElement(commandHandler.getInvocation(1), HelpCommandHandler.COMMAND_NAME_KEY, "abc");
+ }
+
+ /**
+ * Perform initialization before each test
+ *
+ * @see org.mockftpserver.stub.command.AbstractCommandHandlerTest#setUp()
+ */
+ protected void setUp() throws Exception {
+ super.setUp();
+ commandHandler = new HelpCommandHandler();
+ commandHandler.setReplyTextBundle(replyTextBundle);
+ }
+
+}
diff --git a/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/ListCommandHandlerTest.java b/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/ListCommandHandlerTest.java
new file mode 100644
index 0000000..204049c
--- /dev/null
+++ b/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/ListCommandHandlerTest.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.stub.command;
+
+import org.easymock.MockControl;
+import org.mockftpserver.core.command.Command;
+import org.mockftpserver.core.command.CommandNames;
+import org.mockftpserver.core.command.ReplyCodes;
+import org.mockftpserver.stub.command.ListCommandHandler;
+
+/**
+ * Tests for the ListCommandHandler class
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public final class ListCommandHandlerTest extends AbstractCommandHandlerTest {
+
+ private ListCommandHandler commandHandler;
+
+ /**
+ * Test the handleCommand() method
+ * @throws Exception
+ */
+ public void testHandleCommand() throws Exception {
+ final String DIR_LISTING = " directory listing\nabc.txt\ndef.log\n";
+ final String DIR_LISTING_TRIMMED = DIR_LISTING.trim();
+ ((ListCommandHandler)commandHandler).setDirectoryListing(DIR_LISTING);
+
+ for (int i = 0; i < 2; i++) {
+ session.sendReply(ReplyCodes.SEND_DATA_INITIAL_OK, replyTextFor(ReplyCodes.SEND_DATA_INITIAL_OK));
+ session.openDataConnection();
+ byte[] bytes = DIR_LISTING_TRIMMED.getBytes();
+ session.sendData(bytes, bytes.length);
+ control(session).setMatcher(MockControl.ARRAY_MATCHER);
+ session.closeDataConnection();
+ session.sendReply(ReplyCodes.SEND_DATA_FINAL_OK, replyTextFor(ReplyCodes.SEND_DATA_FINAL_OK));
+ }
+ replay(session);
+
+ Command command1 = new Command(CommandNames.LIST, array(DIR1));
+ Command command2 = new Command(CommandNames.LIST, EMPTY);
+ commandHandler.handleCommand(command1, session);
+ commandHandler.handleCommand(command2, session);
+ verify(session);
+
+ verifyNumberOfInvocations(commandHandler, 2);
+ verifyOneDataElement(commandHandler.getInvocation(0), ListCommandHandler.PATHNAME_KEY, DIR1);
+ verifyOneDataElement(commandHandler.getInvocation(1), ListCommandHandler.PATHNAME_KEY, null);
+ }
+
+ /**
+ * Perform initialization before each test
+ * @see org.mockftpserver.stub.command.AbstractCommandHandlerTest#setUp()
+ */
+ protected void setUp() throws Exception {
+ super.setUp();
+ commandHandler = new ListCommandHandler();
+ commandHandler.setReplyTextBundle(replyTextBundle);
+ }
+
+}
diff --git a/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/MkdCommandHandlerTest.java b/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/MkdCommandHandlerTest.java
new file mode 100644
index 0000000..ec10b3e
--- /dev/null
+++ b/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/MkdCommandHandlerTest.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.stub.command;
+
+import org.mockftpserver.core.command.Command;
+import org.mockftpserver.core.command.CommandNames;
+import org.mockftpserver.core.command.ReplyCodes;
+import org.mockftpserver.stub.command.MkdCommandHandler;
+
+/**
+ * Tests for the MkdCommandHandler class
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public final class MkdCommandHandlerTest extends AbstractCommandHandlerTest {
+
+ private MkdCommandHandler commandHandler;
+ private Command command1;
+ private Command command2;
+
+ /**
+ * Test the handleCommand() method
+ */
+ public void testHandleCommand() throws Exception {
+
+ session.sendReply(ReplyCodes.MKD_OK, formattedReplyTextFor(ReplyCodes.MKD_OK, DIR1));
+ session.sendReply(ReplyCodes.MKD_OK, formattedReplyTextFor(ReplyCodes.MKD_OK, DIR2));
+ replay(session);
+
+ commandHandler.handleCommand(command1, session);
+ commandHandler.handleCommand(command2, session);
+ verify(session);
+
+ verifyNumberOfInvocations(commandHandler, 2);
+ verifyOneDataElement(commandHandler.getInvocation(0), MkdCommandHandler.PATHNAME_KEY, DIR1);
+ verifyOneDataElement(commandHandler.getInvocation(1), MkdCommandHandler.PATHNAME_KEY, DIR2);
+ }
+
+ /**
+ * Test the handleCommand() method, when no pathname parameter has been specified
+ */
+ public void testHandleCommand_MissingPathnameParameter() throws Exception {
+ testHandleCommand_InvalidParameters(commandHandler, CommandNames.MKD, EMPTY);
+ }
+
+ /**
+ * Perform initialization before each test
+ *
+ * @see org.mockftpserver.stub.command.AbstractCommandHandlerTest#setUp()
+ */
+ protected void setUp() throws Exception {
+ super.setUp();
+ commandHandler = new MkdCommandHandler();
+ commandHandler.setReplyTextBundle(replyTextBundle);
+ commandHandler.setReplyTextBundle(replyTextBundle);
+ command1 = new Command(CommandNames.MKD, array(DIR1));
+ command2 = new Command(CommandNames.MKD, array(DIR2));
+ }
+
+}
diff --git a/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/ModeCommandHandlerTest.java b/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/ModeCommandHandlerTest.java
new file mode 100644
index 0000000..7d55e46
--- /dev/null
+++ b/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/ModeCommandHandlerTest.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.stub.command;
+
+import org.mockftpserver.core.command.Command;
+import org.mockftpserver.core.command.CommandNames;
+import org.mockftpserver.core.command.ReplyCodes;
+import org.mockftpserver.stub.command.ModeCommandHandler;
+
+/**
+ * Tests for the ModeCommandHandler class
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public final class ModeCommandHandlerTest extends AbstractCommandHandlerTest {
+
+ private static final String CODE1 = "S";
+ private static final String CODE2 = "B";
+
+ private ModeCommandHandler commandHandler;
+ private Command command1;
+ private Command command2;
+
+ /**
+ * Test the handleCommand() method
+ */
+ public void testHandleCommand() throws Exception {
+
+ session.sendReply(ReplyCodes.MODE_OK, replyTextFor(ReplyCodes.MODE_OK));
+ session.sendReply(ReplyCodes.MODE_OK, replyTextFor(ReplyCodes.MODE_OK));
+ replay(session);
+
+ commandHandler.handleCommand(command1, session);
+ commandHandler.handleCommand(command2, session);
+ verify(session);
+
+ verifyNumberOfInvocations(commandHandler, 2);
+ verifyOneDataElement(commandHandler.getInvocation(0), ModeCommandHandler.MODE_KEY, CODE1);
+ verifyOneDataElement(commandHandler.getInvocation(1), ModeCommandHandler.MODE_KEY, CODE2);
+ }
+
+ /**
+ * Test the handleCommand() method, when no pathname parameter has been specified
+ */
+ public void testHandleCommand_MissingPathnameParameter() throws Exception {
+ testHandleCommand_InvalidParameters(commandHandler, CommandNames.MODE, EMPTY);
+ }
+
+ /**
+ * Perform initialization before each test
+ *
+ * @see org.mockftpserver.stub.command.AbstractCommandHandlerTest#setUp()
+ */
+ protected void setUp() throws Exception {
+ super.setUp();
+ commandHandler = new ModeCommandHandler();
+ commandHandler.setReplyTextBundle(replyTextBundle);
+ command1 = new Command(CommandNames.MODE, array(CODE1));
+ command2 = new Command(CommandNames.MODE, array(CODE2));
+ }
+
+}
diff --git a/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/NlstCommandHandlerTest.java b/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/NlstCommandHandlerTest.java
new file mode 100644
index 0000000..2c7968e
--- /dev/null
+++ b/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/NlstCommandHandlerTest.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.stub.command;
+
+import org.easymock.MockControl;
+import org.mockftpserver.core.command.Command;
+import org.mockftpserver.core.command.CommandNames;
+import org.mockftpserver.core.command.ReplyCodes;
+
+/**
+ * Tests for the NlstCommandHandler class
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public final class NlstCommandHandlerTest extends AbstractCommandHandlerTest {
+
+ private NlstCommandHandler commandHandler;
+
+ /**
+ * Test the handleCommand() method
+ */
+ public void testHandleCommand() throws Exception {
+ final String DIR_LISTING = " directory listing\nabc.txt\ndef.log\n";
+ final String DIR_LISTING_TRIMMED = DIR_LISTING.trim();
+ ((NlstCommandHandler)commandHandler).setDirectoryListing(DIR_LISTING);
+
+ for (int i = 0; i < 2; i++) {
+ session.sendReply(ReplyCodes.SEND_DATA_INITIAL_OK, replyTextFor(ReplyCodes.SEND_DATA_INITIAL_OK));
+ session.openDataConnection();
+ byte[] bytes = DIR_LISTING_TRIMMED.getBytes();
+ session.sendData(bytes, bytes.length);
+ control(session).setMatcher(MockControl.ARRAY_MATCHER);
+ session.closeDataConnection();
+ session.sendReply(ReplyCodes.SEND_DATA_FINAL_OK, replyTextFor(ReplyCodes.SEND_DATA_FINAL_OK));
+ }
+ replay(session);
+
+ Command command1 = new Command(CommandNames.LIST, array(DIR1));
+ Command command2 = new Command(CommandNames.LIST, EMPTY);
+ commandHandler.handleCommand(command1, session);
+ commandHandler.handleCommand(command2, session);
+ verify(session);
+
+ verifyNumberOfInvocations(commandHandler, 2);
+ verifyOneDataElement(commandHandler.getInvocation(0), NlstCommandHandler.PATHNAME_KEY, DIR1);
+ verifyOneDataElement(commandHandler.getInvocation(1), NlstCommandHandler.PATHNAME_KEY, null);
+ }
+
+ /**
+ * Perform initialization before each test
+ * @see org.mockftpserver.stub.command.AbstractCommandHandlerTest#setUp()
+ */
+ protected void setUp() throws Exception {
+ super.setUp();
+ commandHandler = new NlstCommandHandler();
+ commandHandler.setReplyTextBundle(replyTextBundle);
+ }
+
+}
diff --git a/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/NoopCommandHandlerTest.java b/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/NoopCommandHandlerTest.java
new file mode 100644
index 0000000..93d0af0
--- /dev/null
+++ b/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/NoopCommandHandlerTest.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.stub.command;
+
+import org.mockftpserver.core.command.Command;
+import org.mockftpserver.core.command.CommandNames;
+import org.mockftpserver.core.command.ReplyCodes;
+import org.mockftpserver.stub.command.NoopCommandHandler;
+
+/**
+ * Tests for the NoopCommandHandler class
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public final class NoopCommandHandlerTest extends AbstractCommandHandlerTest {
+
+ private NoopCommandHandler commandHandler;
+
+ /**
+ * Test the handleCommand() method
+ */
+ public void testHandleCommand() throws Exception {
+ final Command COMMAND = new Command(CommandNames.NOOP, EMPTY);
+
+ session.sendReply(ReplyCodes.NOOP_OK, replyTextFor(ReplyCodes.NOOP_OK));
+ replay(session);
+
+ commandHandler.handleCommand(COMMAND, session);
+ verify(session);
+
+ verifyNumberOfInvocations(commandHandler, 1);
+ verifyNoDataElements(commandHandler.getInvocation(0));
+ }
+
+ /**
+ * Perform initialization before each test
+ *
+ * @see org.mockftpserver.stub.command.AbstractCommandHandlerTest#setUp()
+ */
+ protected void setUp() throws Exception {
+ super.setUp();
+ commandHandler = new NoopCommandHandler();
+ commandHandler.setReplyTextBundle(replyTextBundle);
+ }
+
+}
diff --git a/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/PassCommandHandlerTest.java b/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/PassCommandHandlerTest.java
new file mode 100644
index 0000000..e6baa54
--- /dev/null
+++ b/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/PassCommandHandlerTest.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.stub.command;
+
+import org.mockftpserver.core.command.Command;
+import org.mockftpserver.core.command.CommandNames;
+import org.mockftpserver.core.command.ReplyCodes;
+import org.mockftpserver.stub.command.PassCommandHandler;
+
+/**
+ * Tests for the PassCommandHandler class
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public final class PassCommandHandlerTest extends AbstractCommandHandlerTest {
+
+ private static final String PASSWORD1 = "password1";
+ private static final String PASSWORD2 = "password2";
+
+ private PassCommandHandler commandHandler;
+ private Command command1;
+ private Command command2;
+
+ /**
+ * Test the handleCommand() method
+ */
+ public void testHandleCommand() throws Exception {
+
+ session.sendReply(ReplyCodes.PASS_OK, replyTextFor(ReplyCodes.PASS_OK));
+ session.sendReply(ReplyCodes.PASS_OK, replyTextFor(ReplyCodes.PASS_OK));
+ replay(session);
+
+ commandHandler.handleCommand(command1, session);
+ commandHandler.handleCommand(command2, session);
+ verify(session);
+
+ verifyNumberOfInvocations(commandHandler, 2);
+ verifyOneDataElement(commandHandler.getInvocation(0), PassCommandHandler.PASSWORD_KEY, PASSWORD1);
+ verifyOneDataElement(commandHandler.getInvocation(1), PassCommandHandler.PASSWORD_KEY, PASSWORD2);
+ }
+
+ /**
+ * Test the handleCommand() method, when no password parameter has been specified
+ */
+ public void testHandleCommand_MissingPasswordParameter() throws Exception {
+ testHandleCommand_InvalidParameters(commandHandler, CommandNames.PASS, EMPTY);
+ }
+
+ /**
+ * Perform initialization before each test
+ * @see org.mockftpserver.stub.command.AbstractCommandHandlerTest#setUp()
+ */
+ protected void setUp() throws Exception {
+ super.setUp();
+ commandHandler = new PassCommandHandler();
+ commandHandler.setReplyTextBundle(replyTextBundle);
+ command1 = new Command(CommandNames.PASS, array(PASSWORD1));
+ command2 = new Command(CommandNames.PASS, array(PASSWORD2));
+ }
+
+}
diff --git a/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/PasvCommandHandlerTest.java b/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/PasvCommandHandlerTest.java
new file mode 100644
index 0000000..0366d3c
--- /dev/null
+++ b/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/PasvCommandHandlerTest.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.stub.command;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+
+import org.apache.log4j.Logger;
+import org.mockftpserver.core.command.Command;
+import org.mockftpserver.core.command.CommandNames;
+import org.mockftpserver.core.command.ReplyCodes;
+import org.mockftpserver.stub.command.PasvCommandHandler;
+
+/**
+ * Tests for the PasvCommandHandler class
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public final class PasvCommandHandlerTest extends AbstractCommandHandlerTest {
+
+ private static final Logger LOG = Logger.getLogger(PasvCommandHandlerTest.class);
+ private static final int PORT = (23 << 8) + 77;
+
+ private PasvCommandHandler commandHandler;
+
+ /**
+ * Test the handleCommand() method
+ */
+ public void testHandleCommand() throws Exception {
+
+ final InetAddress SERVER = inetAddress("192.168.0.2");
+ session.switchToPassiveMode();
+ control(session).setReturnValue(PORT);
+ session.getServerHost();
+ control(session).setReturnValue(SERVER);
+ session.sendReply(ReplyCodes.PASV_OK, formattedReplyTextFor(227, "(192,168,0,2,23,77)"));
+ replay(session);
+
+ final Command COMMAND = new Command(CommandNames.PASV, EMPTY);
+
+ commandHandler.handleCommand(COMMAND, session);
+ verify(session);
+
+ verifyNumberOfInvocations(commandHandler, 1);
+ verifyNoDataElements(commandHandler.getInvocation(0));
+ }
+
+ /**
+ * Test convertHostAndPortToStringOfBytes() method
+ */
+ public void testConvertHostAndPortToStringOfBytes() throws UnknownHostException {
+ InetAddress host = InetAddress.getByName("196.168.44.55");
+ String result = PasvCommandHandler.convertHostAndPortToStringOfBytes(host, PORT);
+ LOG.info("result=" + result);
+ assertEquals("result", "196,168,44,55,23,77", result);
+ }
+
+ /**
+ * Perform initialization before each test
+ * @see org.mockftpserver.stub.command.AbstractCommandHandlerTest#setUp()
+ */
+ protected void setUp() throws Exception {
+ super.setUp();
+ commandHandler = new PasvCommandHandler();
+ commandHandler.setReplyTextBundle(replyTextBundle);
+ }
+
+}
diff --git a/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/PortCommandHandlerTest.java b/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/PortCommandHandlerTest.java
new file mode 100644
index 0000000..cdaad25
--- /dev/null
+++ b/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/PortCommandHandlerTest.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.stub.command;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+import org.apache.log4j.Logger;
+import org.mockftpserver.core.command.Command;
+import org.mockftpserver.core.command.CommandNames;
+import org.mockftpserver.core.command.ReplyCodes;
+import org.mockftpserver.core.util.AssertFailedException;
+import org.mockftpserver.stub.command.PortCommandHandler;
+
+/**
+ * Tests for the PortCommandHandler class
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public final class PortCommandHandlerTest extends AbstractCommandHandlerTest {
+
+ private static final Logger LOG = Logger.getLogger(PortCommandHandlerTest.class);
+ private static final String[] PARAMETERS = new String[] { "192", "22", "250", "44", "1", "206" };
+ private static final String[] PARAMETERS_INSUFFICIENT = new String[] {"7", "29", "99", "11", "77"};
+ private static final int PORT = (1 << 8) + 206;
+ private static final InetAddress HOST = inetAddress("192.22.250.44");
+
+ private PortCommandHandler commandHandler;
+
+ /**
+ * Test the handleCommand() method
+ */
+ public void testHandleCommand() throws Exception {
+ final Command COMMAND = new Command(CommandNames.PORT, PARAMETERS);
+
+ session.setClientDataPort(PORT);
+ session.setClientDataHost(HOST);
+ session.sendReply(ReplyCodes.PORT_OK, replyTextFor(ReplyCodes.PORT_OK));
+ replay(session);
+
+ commandHandler.handleCommand(COMMAND, session);
+ verify(session);
+
+ verifyNumberOfInvocations(commandHandler, 1);
+ verifyTwoDataElements(commandHandler.getInvocation(0),
+ PortCommandHandler.HOST_KEY, HOST,
+ PortCommandHandler.PORT_KEY, new Integer(PORT));
+ }
+
+ /**
+ * Test the handleCommand() method, when not enough parameters have been specified
+ */
+ public void testHandleCommand_InsufficientParameters() throws Exception {
+ testHandleCommand_InvalidParameters(commandHandler, CommandNames.PORT, PARAMETERS_INSUFFICIENT);
+ }
+
+ /**
+ * Test the parseHost() method
+ * @throws UnknownHostException
+ */
+ public void testParseHost() throws UnknownHostException {
+ InetAddress host = PortCommandHandler.parseHost(PARAMETERS);
+ assertEquals("InetAddress", HOST, host);
+ }
+
+ /**
+ * Test the parsePortNumber() method
+ */
+ public void testParsePortNumber() {
+ int portNumber = PortCommandHandler.parsePortNumber(PARAMETERS);
+ assertEquals("portNumber", PORT, portNumber);
+ }
+
+ /**
+ * Test the parseHost() method, passing in null
+ * @throws UnknownHostException
+ */
+ public void testParseHost_Null() throws UnknownHostException {
+ try {
+ PortCommandHandler.parseHost(null);
+ fail("Expected AssertFailedException");
+ }
+ catch (AssertFailedException expected) {
+ LOG.info("Expected: " + expected);
+ }
+ }
+
+ /**
+ * Test the parseHost() method, passing in a String[] with not enough parameters
+ * @throws UnknownHostException
+ */
+ public void testParseHost_InsufficientParameters() throws UnknownHostException {
+ try {
+ PortCommandHandler.parseHost(PARAMETERS_INSUFFICIENT);
+ fail("Expected AssertFailedException");
+ }
+ catch (AssertFailedException expected) {
+ LOG.info("Expected: " + expected);
+ }
+ }
+
+ /**
+ * Test the parsePortNumber() method, passing in null
+ * @throws UnknownHostException
+ */
+ public void testParsePortNumber_Null() throws UnknownHostException {
+ try {
+ PortCommandHandler.parsePortNumber(null);
+ fail("Expected AssertFailedException");
+ }
+ catch (AssertFailedException expected) {
+ LOG.info("Expected: " + expected);
+ }
+ }
+
+ /**
+ * Test the parsePortNumber() method, passing in a String[] with not enough parameters
+ * @throws UnknownHostException
+ */
+ public void testParsePortNumber_InsufficientParameters() throws UnknownHostException {
+ try {
+ PortCommandHandler.parsePortNumber(PARAMETERS_INSUFFICIENT);
+ fail("Expected AssertFailedException");
+ }
+ catch (AssertFailedException expected) {
+ LOG.info("Expected: " + expected);
+ }
+ }
+
+ /**
+ * Perform initialization before each test
+ *
+ * @see org.mockftpserver.stub.command.AbstractCommandHandlerTest#setUp()
+ */
+ protected void setUp() throws Exception {
+ super.setUp();
+ commandHandler = new PortCommandHandler();
+ commandHandler.setReplyTextBundle(replyTextBundle);
+ }
+
+}
diff --git a/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/PwdCommandHandlerTest.java b/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/PwdCommandHandlerTest.java
new file mode 100644
index 0000000..e3e6f4a
--- /dev/null
+++ b/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/PwdCommandHandlerTest.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.stub.command;
+
+import org.mockftpserver.core.command.Command;
+import org.mockftpserver.core.command.CommandNames;
+import org.mockftpserver.core.command.ReplyCodes;
+import org.mockftpserver.stub.command.PwdCommandHandler;
+
+/**
+ * Tests for the PwdCommandHandler class
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public final class PwdCommandHandlerTest extends AbstractCommandHandlerTest {
+
+ private PwdCommandHandler commandHandler;
+
+ /**
+ * Test the handleCommand() method
+ */
+ public void testHandleCommand() throws Exception {
+
+ final String RESPONSE_DATA = "current dir 1";
+ commandHandler.setDirectory(RESPONSE_DATA);
+
+ session.sendReply(ReplyCodes.PWD_OK, formattedReplyTextFor(ReplyCodes.PWD_OK, "\"" + RESPONSE_DATA + "\""));
+ replay(session);
+
+ final Command COMMAND = new Command(CommandNames.PWD, EMPTY);
+
+ commandHandler.handleCommand(COMMAND, session);
+ verify(session);
+
+ verifyNumberOfInvocations(commandHandler, 1);
+ verifyNoDataElements(commandHandler.getInvocation(0));
+ }
+
+ /**
+ * Perform initialization before each test
+ *
+ * @see org.mockftpserver.stub.command.AbstractCommandHandlerTest#setUp()
+ */
+ protected void setUp() throws Exception {
+ super.setUp();
+ commandHandler = new PwdCommandHandler();
+ commandHandler.setReplyTextBundle(replyTextBundle);
+ }
+
+}
diff --git a/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/QuitCommandHandlerTest.java b/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/QuitCommandHandlerTest.java
new file mode 100644
index 0000000..c9bbe26
--- /dev/null
+++ b/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/QuitCommandHandlerTest.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.stub.command;
+
+import org.mockftpserver.core.command.Command;
+import org.mockftpserver.core.command.CommandNames;
+import org.mockftpserver.core.command.ReplyCodes;
+import org.mockftpserver.stub.command.QuitCommandHandler;
+
+/**
+ * Tests for the QuitCommandHandler class
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public final class QuitCommandHandlerTest extends AbstractCommandHandlerTest {
+
+ private QuitCommandHandler commandHandler;
+
+ /**
+ * Test the handleCommand() method
+ */
+ public void testHandleCommand() throws Exception {
+ final Command COMMAND = new Command(CommandNames.QUIT, EMPTY);
+
+ session.sendReply(ReplyCodes.QUIT_OK, replyTextFor(ReplyCodes.QUIT_OK));
+ session.close();
+ replay(session);
+
+ commandHandler.handleCommand(COMMAND, session);
+ verify(session);
+
+ verifyNumberOfInvocations(commandHandler, 1);
+ verifyNoDataElements(commandHandler.getInvocation(0));
+ }
+
+ /**
+ * Perform initialization before each test
+ *
+ * @see org.mockftpserver.stub.command.AbstractCommandHandlerTest#setUp()
+ */
+ protected void setUp() throws Exception {
+ super.setUp();
+ commandHandler = new QuitCommandHandler();
+ commandHandler.setReplyTextBundle(replyTextBundle);
+ }
+
+}
diff --git a/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/ReinCommandHandlerTest.java b/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/ReinCommandHandlerTest.java
new file mode 100644
index 0000000..505fd8b
--- /dev/null
+++ b/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/ReinCommandHandlerTest.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.stub.command;
+
+import org.mockftpserver.core.command.Command;
+import org.mockftpserver.core.command.CommandNames;
+import org.mockftpserver.core.command.ReplyCodes;
+
+/**
+ * Tests for the ReinCommandHandler class
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public final class ReinCommandHandlerTest extends AbstractCommandHandlerTest {
+
+ private ReinCommandHandler commandHandler;
+ private Command command1;
+ private Command command2;
+
+ /**
+ * Test the handleCommand(Command,Session) method
+ * @throws Exception
+ */
+ public void testHandleCommand() throws Exception {
+ session.sendReply(ReplyCodes.REIN_OK, replyTextFor(ReplyCodes.REIN_OK));
+ session.sendReply(ReplyCodes.REIN_OK, replyTextFor(ReplyCodes.REIN_OK));
+ replay(session);
+
+ commandHandler.handleCommand(command1, session);
+ commandHandler.handleCommand(command2, session);
+ verify(session);
+
+ verifyNumberOfInvocations(commandHandler, 2);
+ verifyNoDataElements(commandHandler.getInvocation(0));
+ verifyNoDataElements(commandHandler.getInvocation(1));
+ }
+
+ /**
+ * Perform initialization before each test
+ * @see org.mockftpserver.stub.command.AbstractCommandHandlerTest#setUp()
+ */
+ protected void setUp() throws Exception {
+ super.setUp();
+ commandHandler = new ReinCommandHandler();
+ commandHandler.setReplyTextBundle(replyTextBundle);
+ command1 = new Command(CommandNames.REIN, EMPTY);
+ command2 = new Command(CommandNames.REIN, EMPTY);
+ }
+}
diff --git a/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/RestCommandHandlerTest.java b/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/RestCommandHandlerTest.java
new file mode 100644
index 0000000..820f6fe
--- /dev/null
+++ b/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/RestCommandHandlerTest.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.stub.command;
+
+import org.mockftpserver.core.command.Command;
+import org.mockftpserver.core.command.CommandNames;
+import org.mockftpserver.core.command.ReplyCodes;
+import org.mockftpserver.stub.command.RestCommandHandler;
+
+/**
+ * Tests for the RestCommandHandler class
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public final class RestCommandHandlerTest extends AbstractCommandHandlerTest {
+
+ private static final String MARKER1 = "marker1";
+ private static final String MARKER2 = "marker2";
+
+ private RestCommandHandler commandHandler;
+ private Command command1;
+ private Command command2;
+
+ /**
+ * Test the handleCommand() method
+ */
+ public void testHandleCommand() throws Exception {
+
+ session.sendReply(ReplyCodes.REST_OK, replyTextFor(ReplyCodes.REST_OK));
+ session.sendReply(ReplyCodes.REST_OK, replyTextFor(ReplyCodes.REST_OK));
+ replay(session);
+
+ commandHandler.handleCommand(command1, session);
+ commandHandler.handleCommand(command2, session);
+ verify(session);
+
+ verifyNumberOfInvocations(commandHandler, 2);
+ verifyOneDataElement(commandHandler.getInvocation(0), RestCommandHandler.MARKER_KEY, MARKER1);
+ verifyOneDataElement(commandHandler.getInvocation(1), RestCommandHandler.MARKER_KEY, MARKER2);
+ }
+
+ /**
+ * Test the handleCommand() method, when no marker parameter has been specified
+ */
+ public void testHandleCommand_MissingPathnameParameter() throws Exception {
+ testHandleCommand_InvalidParameters(commandHandler, CommandNames.REST, EMPTY);
+ }
+
+ /**
+ * Perform initialization before each test
+ *
+ * @see org.mockftpserver.stub.command.AbstractCommandHandlerTest#setUp()
+ */
+ protected void setUp() throws Exception {
+ super.setUp();
+ commandHandler = new RestCommandHandler();
+ commandHandler.setReplyTextBundle(replyTextBundle);
+ commandHandler.setReplyTextBundle(replyTextBundle);
+ command1 = new Command(CommandNames.REST, array(MARKER1));
+ command2 = new Command(CommandNames.REST, array(MARKER2));
+ }
+
+}
diff --git a/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/RetrCommandHandlerTest.java b/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/RetrCommandHandlerTest.java
new file mode 100644
index 0000000..8fb4572
--- /dev/null
+++ b/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/RetrCommandHandlerTest.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.stub.command;
+
+import org.apache.log4j.Logger;
+import org.easymock.MockControl;
+import org.mockftpserver.core.command.Command;
+import org.mockftpserver.core.command.CommandNames;
+import org.mockftpserver.core.command.ReplyCodes;
+import org.mockftpserver.core.util.AssertFailedException;
+import org.mockftpserver.stub.command.RetrCommandHandler;
+
+/**
+ * Tests for the RetrCommandHandler class
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public final class RetrCommandHandlerTest extends AbstractCommandHandlerTest {
+
+ private static final Logger LOG = Logger.getLogger(RetrCommandHandlerTest.class);
+
+ private RetrCommandHandler commandHandler;
+
+ /**
+ * Test the constructor that takes a String, passing in a null
+ */
+ public void testConstructor_String_Null() {
+ try {
+ new RetrCommandHandler((String)null);
+ fail("Expected AssertFailedException");
+ }
+ catch (AssertFailedException expected) {
+ LOG.info("Expected: " + expected);
+ }
+ }
+
+ /**
+ * Test the constructor that takes a byte[], passing in a null
+ */
+ public void testConstructor_ByteArray_Null() {
+ try {
+ new RetrCommandHandler((byte[])null);
+ fail("Expected AssertFailedException");
+ }
+ catch (AssertFailedException expected) {
+ LOG.info("Expected: " + expected);
+ }
+ }
+
+ /**
+ * Test the setFileContents(String) method, passing in a null
+ */
+ public void testSetFileContents_String_Null() {
+ try {
+ commandHandler.setFileContents((String)null);
+ fail("Expected AssertFailedException");
+ }
+ catch (AssertFailedException expected) {
+ LOG.info("Expected: " + expected);
+ }
+ }
+
+ /**
+ * Test the setFileContents(byte[]) method, passing in a null
+ */
+ public void testSetFileContents_ByteArray_Null() {
+ try {
+ commandHandler.setFileContents((byte[])null);
+ fail("Expected AssertFailedException");
+ }
+ catch (AssertFailedException expected) {
+ LOG.info("Expected: " + expected);
+ }
+ }
+
+ /**
+ * Test the handleCommand() method
+ * @throws Exception
+ */
+ public void testHandleCommand() throws Exception {
+ final String FILE_CONTENTS = "abc_123 456";
+ commandHandler.setFileContents(FILE_CONTENTS);
+
+ session.sendReply(ReplyCodes.SEND_DATA_INITIAL_OK, replyTextFor(ReplyCodes.SEND_DATA_INITIAL_OK));
+ session.openDataConnection();
+ session.sendData(FILE_CONTENTS.getBytes(), FILE_CONTENTS.length());
+ control(session).setMatcher(MockControl.ARRAY_MATCHER);
+ session.closeDataConnection();
+ session.sendReply(ReplyCodes.SEND_DATA_FINAL_OK, replyTextFor(ReplyCodes.SEND_DATA_FINAL_OK));
+ replay(session);
+
+ Command command = new Command(CommandNames.RETR, array(FILENAME1));
+ commandHandler.handleCommand(command, session);
+ verify(session);
+
+ verifyNumberOfInvocations(commandHandler, 1);
+ verifyOneDataElement(commandHandler.getInvocation(0), RetrCommandHandler.PATHNAME_KEY, FILENAME1);
+ }
+
+ /**
+ * Test the handleCommand() method, when no pathname parameter has been specified
+ */
+ public void testHandleCommand_MissingPathnameParameter() throws Exception {
+ testHandleCommand_InvalidParameters(commandHandler, CommandNames.RETR, EMPTY);
+ }
+
+ /**
+ * Perform initialization before each test
+ * @see org.mockftpserver.stub.command.AbstractCommandHandlerTest#setUp()
+ */
+ protected void setUp() throws Exception {
+ super.setUp();
+ commandHandler = new RetrCommandHandler();
+ commandHandler.setReplyTextBundle(replyTextBundle);
+ }
+
+}
diff --git a/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/RmdCommandHandlerTest.java b/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/RmdCommandHandlerTest.java
new file mode 100644
index 0000000..1464eb8
--- /dev/null
+++ b/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/RmdCommandHandlerTest.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.stub.command;
+
+import org.mockftpserver.core.command.Command;
+import org.mockftpserver.core.command.CommandNames;
+import org.mockftpserver.core.command.ReplyCodes;
+import org.mockftpserver.stub.command.RmdCommandHandler;
+
+/**
+ * Tests for the RmdCommandHandler class
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public final class RmdCommandHandlerTest extends AbstractCommandHandlerTest {
+
+ private RmdCommandHandler commandHandler;
+ private Command command1;
+ private Command command2;
+
+ /**
+ * Test the handleCommand() method
+ */
+ public void testHandleCommand() throws Exception {
+ session.sendReply(ReplyCodes.RMD_OK, replyTextFor(ReplyCodes.RMD_OK));
+ session.sendReply(ReplyCodes.RMD_OK, replyTextFor(ReplyCodes.RMD_OK));
+ replay(session);
+
+ commandHandler.handleCommand(command1, session);
+ commandHandler.handleCommand(command2, session);
+ verify(session);
+
+ verifyNumberOfInvocations(commandHandler, 2);
+ verifyOneDataElement(commandHandler.getInvocation(0), RmdCommandHandler.PATHNAME_KEY, DIR1);
+ verifyOneDataElement(commandHandler.getInvocation(1), RmdCommandHandler.PATHNAME_KEY, DIR2);
+ }
+
+ /**
+ * Test the handleCommand() method, when no pathname parameter has been specified
+ */
+ public void testHandleCommand_MissingPathnameParameter() throws Exception {
+ testHandleCommand_InvalidParameters(commandHandler, CommandNames.RMD, EMPTY);
+ }
+
+ /**
+ * Perform initialization before each test
+ * @see org.mockftpserver.stub.command.AbstractCommandHandlerTest#setUp()
+ */
+ protected void setUp() throws Exception {
+ super.setUp();
+ commandHandler = new RmdCommandHandler();
+ commandHandler.setReplyTextBundle(replyTextBundle);
+ command1 = new Command(CommandNames.RMD, array(DIR1));
+ command2 = new Command(CommandNames.RMD, array(DIR2));
+ }
+
+}
diff --git a/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/RnfrCommandHandlerTest.java b/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/RnfrCommandHandlerTest.java
new file mode 100644
index 0000000..cbf2d31
--- /dev/null
+++ b/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/RnfrCommandHandlerTest.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.stub.command;
+
+import org.mockftpserver.core.command.Command;
+import org.mockftpserver.core.command.CommandNames;
+import org.mockftpserver.core.command.ReplyCodes;
+import org.mockftpserver.stub.command.RnfrCommandHandler;
+
+/**
+ * Tests for the RnfrCommandHandler class
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public final class RnfrCommandHandlerTest extends AbstractCommandHandlerTest {
+
+ private RnfrCommandHandler commandHandler;
+ private Command command1;
+ private Command command2;
+
+ /**
+ * Test the handleCommand() method
+ */
+ public void testHandleCommand() throws Exception {
+
+ session.sendReply(ReplyCodes.RNFR_OK, replyTextFor(ReplyCodes.RNFR_OK));
+ session.sendReply(ReplyCodes.RNFR_OK, replyTextFor(ReplyCodes.RNFR_OK));
+ replay(session);
+
+ commandHandler.handleCommand(command1, session);
+ commandHandler.handleCommand(command2, session);
+ verify(session);
+
+ verifyNumberOfInvocations(commandHandler, 2);
+ verifyOneDataElement(commandHandler.getInvocation(0), RnfrCommandHandler.PATHNAME_KEY, FILENAME1);
+ verifyOneDataElement(commandHandler.getInvocation(1), RnfrCommandHandler.PATHNAME_KEY, FILENAME2);
+ }
+
+ /**
+ * Test the handleCommand() method, when no pathname parameter has been specified
+ */
+ public void testHandleCommand_MissingPathnameParameter() throws Exception {
+ testHandleCommand_InvalidParameters(commandHandler, CommandNames.RNFR, EMPTY);
+ }
+
+ /**
+ * Perform initialization before each test
+ *
+ * @see org.mockftpserver.stub.command.AbstractCommandHandlerTest#setUp()
+ */
+ protected void setUp() throws Exception {
+ super.setUp();
+ commandHandler = new RnfrCommandHandler();
+ commandHandler.setReplyTextBundle(replyTextBundle);
+ command1 = new Command(CommandNames.RNFR, array(FILENAME1));
+ command2 = new Command(CommandNames.RNFR, array(FILENAME2));
+ }
+
+}
diff --git a/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/RntoCommandHandlerTest.java b/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/RntoCommandHandlerTest.java
new file mode 100644
index 0000000..d6d1283
--- /dev/null
+++ b/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/RntoCommandHandlerTest.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.stub.command;
+
+import org.mockftpserver.core.command.Command;
+import org.mockftpserver.core.command.CommandNames;
+import org.mockftpserver.core.command.ReplyCodes;
+import org.mockftpserver.stub.command.RntoCommandHandler;
+
+/**
+ * Tests for the RntoCommandHandler class
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public final class RntoCommandHandlerTest extends AbstractCommandHandlerTest {
+
+ private RntoCommandHandler commandHandler;
+ private Command command1;
+ private Command command2;
+
+ /**
+ * Test the handleCommand() method
+ */
+ public void testHandleCommand() throws Exception {
+
+ session.sendReply(ReplyCodes.RNTO_OK, replyTextFor(ReplyCodes.RNTO_OK));
+ session.sendReply(ReplyCodes.RNTO_OK, replyTextFor(ReplyCodes.RNTO_OK));
+ replay(session);
+
+ commandHandler.handleCommand(command1, session);
+ commandHandler.handleCommand(command2, session);
+ verify(session);
+
+ verifyNumberOfInvocations(commandHandler, 2);
+ verifyOneDataElement(commandHandler.getInvocation(0), RntoCommandHandler.PATHNAME_KEY, FILENAME1);
+ verifyOneDataElement(commandHandler.getInvocation(1), RntoCommandHandler.PATHNAME_KEY, FILENAME2);
+ }
+
+ /**
+ * Test the handleCommand() method, when no pathname parameter has been specified
+ */
+ public void testHandleCommand_MissingPathnameParameter() throws Exception {
+ testHandleCommand_InvalidParameters(commandHandler, CommandNames.RNTO, EMPTY);
+ }
+
+ /**
+ * Perform initialization before each test
+ *
+ * @see org.mockftpserver.stub.command.AbstractCommandHandlerTest#setUp()
+ */
+ protected void setUp() throws Exception {
+ super.setUp();
+ commandHandler = new RntoCommandHandler();
+ commandHandler.setReplyTextBundle(replyTextBundle);
+ command1 = new Command(CommandNames.RNTO, array(FILENAME1));
+ command2 = new Command(CommandNames.RNTO, array(FILENAME2));
+ }
+
+}
diff --git a/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/SiteCommandHandlerTest.java b/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/SiteCommandHandlerTest.java
new file mode 100644
index 0000000..d242fc3
--- /dev/null
+++ b/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/SiteCommandHandlerTest.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.stub.command;
+
+import org.mockftpserver.core.command.Command;
+import org.mockftpserver.core.command.CommandNames;
+import org.mockftpserver.core.command.ReplyCodes;
+
+/**
+ * Tests for the SiteCommandHandler class
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public final class SiteCommandHandlerTest extends AbstractCommandHandlerTest {
+
+ private static final String PARAMETERS1 = "abc def";
+ private static final String PARAMETERS2 = "abc,23,def";
+
+ private SiteCommandHandler commandHandler;
+ private Command command1;
+ private Command command2;
+
+ /**
+ * Test the handleCommand(Command,Session) method
+ * @throws Exception
+ */
+ public void testHandleCommand() throws Exception {
+ session.sendReply(ReplyCodes.SITE_OK, replyTextFor(ReplyCodes.SITE_OK));
+ session.sendReply(ReplyCodes.SITE_OK, replyTextFor(ReplyCodes.SITE_OK));
+ replay(session);
+
+ commandHandler.handleCommand(command1, session);
+ commandHandler.handleCommand(command2, session);
+ verify(session);
+
+ verifyNumberOfInvocations(commandHandler, 2);
+ verifyOneDataElement(commandHandler.getInvocation(0), SiteCommandHandler.PARAMETERS_KEY, PARAMETERS1);
+ verifyOneDataElement(commandHandler.getInvocation(1), SiteCommandHandler.PARAMETERS_KEY, PARAMETERS2);
+ }
+
+ /**
+ * Test the handleCommand() method, when no "parameters" parameter has been specified
+ */
+ public void testHandleCommand_MissingParameters() throws Exception {
+ testHandleCommand_InvalidParameters(commandHandler, CommandNames.SITE, EMPTY);
+ }
+
+ /**
+ * Perform initialization before each test
+ * @see org.mockftpserver.stub.command.AbstractCommandHandlerTest#setUp()
+ */
+ protected void setUp() throws Exception {
+ super.setUp();
+ commandHandler = new SiteCommandHandler();
+ commandHandler.setReplyTextBundle(replyTextBundle);
+ command1 = new Command(CommandNames.SITE, array(PARAMETERS1));
+ command2 = new Command(CommandNames.SITE, array(PARAMETERS2));
+ }
+}
diff --git a/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/SmntCommandHandlerTest.java b/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/SmntCommandHandlerTest.java
new file mode 100644
index 0000000..68d5a75
--- /dev/null
+++ b/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/SmntCommandHandlerTest.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.stub.command;
+
+import org.mockftpserver.core.command.Command;
+import org.mockftpserver.core.command.CommandNames;
+import org.mockftpserver.core.command.ReplyCodes;
+
+/**
+ * Tests for the SmntCommandHandler class
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public final class SmntCommandHandlerTest extends AbstractCommandHandlerTest {
+
+ private SmntCommandHandler commandHandler;
+ private Command command1;
+ private Command command2;
+
+ /**
+ * Test the handleCommand(Command,Session) method
+ * @throws Exception
+ */
+ public void testHandleCommand() throws Exception {
+ session.sendReply(ReplyCodes.SMNT_OK, replyTextFor(ReplyCodes.SMNT_OK));
+ session.sendReply(ReplyCodes.SMNT_OK, replyTextFor(ReplyCodes.SMNT_OK));
+ replay(session);
+
+ commandHandler.handleCommand(command1, session);
+ commandHandler.handleCommand(command2, session);
+ verify(session);
+
+ verifyNumberOfInvocations(commandHandler, 2);
+ verifyOneDataElement(commandHandler.getInvocation(0), SmntCommandHandler.PATHNAME_KEY, DIR1);
+ verifyOneDataElement(commandHandler.getInvocation(1), SmntCommandHandler.PATHNAME_KEY, DIR2);
+ }
+
+ /**
+ * Test the handleCommand() method, when no pathname parameter has been specified
+ */
+ public void testHandleCommand_MissingPathnameParameter() throws Exception {
+ testHandleCommand_InvalidParameters(commandHandler, CommandNames.SMNT, EMPTY);
+ }
+
+ /**
+ * Perform initialization before each test
+ * @see org.mockftpserver.stub.command.AbstractCommandHandlerTest#setUp()
+ */
+ protected void setUp() throws Exception {
+ super.setUp();
+ commandHandler = new SmntCommandHandler();
+ commandHandler.setReplyTextBundle(replyTextBundle);
+ command1 = new Command(CommandNames.SMNT, array(DIR1));
+ command2 = new Command(CommandNames.SMNT, array(DIR2));
+ }
+}
diff --git a/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/StatCommandHandlerTest.java b/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/StatCommandHandlerTest.java
new file mode 100644
index 0000000..5f57d7c
--- /dev/null
+++ b/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/StatCommandHandlerTest.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.stub.command;
+
+import org.mockftpserver.core.command.Command;
+import org.mockftpserver.core.command.CommandNames;
+import org.mockftpserver.core.command.ReplyCodes;
+import org.mockftpserver.stub.command.StatCommandHandler;
+
+/**
+ * Tests for the StatCommandHandler class
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public final class StatCommandHandlerTest extends AbstractCommandHandlerTest {
+
+ private static final String RESPONSE_DATA = "status info 123.456";
+ private static final String PATHNAME = "dir/file";
+
+ private StatCommandHandler commandHandler;
+
+ /**
+ * Test the handleCommand() method, when no pathname parameter is specified
+ */
+ public void testHandleCommand_NoPathname() throws Exception {
+
+ session.sendReply(ReplyCodes.STAT_SYSTEM_OK, formattedReplyTextFor(ReplyCodes.STAT_SYSTEM_OK, RESPONSE_DATA));
+ replay(session);
+
+ final Command COMMAND = new Command(CommandNames.STAT, EMPTY);
+ commandHandler.setStatus(RESPONSE_DATA);
+ commandHandler.handleCommand(COMMAND, session);
+ verify(session);
+
+ verifyNumberOfInvocations(commandHandler, 1);
+ verifyOneDataElement(commandHandler.getInvocation(0), StatCommandHandler.PATHNAME_KEY, null);
+ }
+
+ /**
+ * Test the handleCommand() method, specifying a pathname parameter
+ * @throws Exception
+ */
+ public void testHandleCommand_Pathname() throws Exception {
+
+ session.sendReply(ReplyCodes.STAT_FILE_OK, formattedReplyTextFor(ReplyCodes.STAT_FILE_OK, RESPONSE_DATA));
+ replay(session);
+
+ final Command COMMAND = new Command(CommandNames.STAT, array(PATHNAME));
+
+ commandHandler.setStatus(RESPONSE_DATA);
+ commandHandler.handleCommand(COMMAND, session);
+ verify(session);
+
+ verifyNumberOfInvocations(commandHandler, 1);
+ verifyOneDataElement(commandHandler.getInvocation(0), StatCommandHandler.PATHNAME_KEY, PATHNAME);
+ }
+
+ /**
+ * Test the handleCommand() method, when the replyCode is explicitly set
+ */
+ public void testHandleCommand_OverrideReplyCode() throws Exception {
+
+ session.sendReply(200, replyTextFor(200));
+ replay(session);
+
+ final Command COMMAND = new Command(CommandNames.STAT, EMPTY);
+ commandHandler.setStatus(RESPONSE_DATA);
+ commandHandler.setReplyCode(200);
+ commandHandler.handleCommand(COMMAND, session);
+
+ verify(session);
+
+ verifyNumberOfInvocations(commandHandler, 1);
+ verifyOneDataElement(commandHandler.getInvocation(0), StatCommandHandler.PATHNAME_KEY, null);
+ }
+
+ /**
+ * Perform initialization before each test
+ *
+ * @see org.mockftpserver.stub.command.AbstractCommandHandlerTest#setUp()
+ */
+ protected void setUp() throws Exception {
+ super.setUp();
+ commandHandler = new StatCommandHandler();
+ commandHandler.setReplyTextBundle(replyTextBundle);
+ }
+
+}
diff --git a/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/StorCommandHandlerTest.java b/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/StorCommandHandlerTest.java
new file mode 100644
index 0000000..3459d0b
--- /dev/null
+++ b/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/StorCommandHandlerTest.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.stub.command;
+
+import org.mockftpserver.core.command.Command;
+import org.mockftpserver.core.command.CommandNames;
+import org.mockftpserver.core.command.ReplyCodes;
+import org.mockftpserver.stub.command.StorCommandHandler;
+
+/**
+ * Tests for the StorCommandHandler class
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public final class StorCommandHandlerTest extends AbstractCommandHandlerTest {
+
+ private StorCommandHandler commandHandler;
+
+ /**
+ * Perform initialization before each test
+ *
+ * @see org.mockftpserver.stub.command.AbstractCommandHandlerTest#setUp()
+ */
+ protected void setUp() throws Exception {
+ super.setUp();
+ commandHandler = new StorCommandHandler();
+ commandHandler.setReplyTextBundle(replyTextBundle);
+ }
+
+ /**
+ * Test the handleCommand() method, as well as the getFileContents() and clearFileContents() methods
+ */
+ public void testHandleCommand() throws Exception {
+ final String DATA = "ABC";
+
+ session.sendReply(ReplyCodes.SEND_DATA_INITIAL_OK, replyTextFor(ReplyCodes.SEND_DATA_INITIAL_OK));
+ session.openDataConnection();
+ session.readData();
+ control(session).setReturnValue(DATA.getBytes());
+ session.closeDataConnection();
+ session.sendReply(ReplyCodes.SEND_DATA_FINAL_OK, replyTextFor(ReplyCodes.SEND_DATA_FINAL_OK));
+ replay(session);
+
+ Command command = new Command(CommandNames.STOR, array(FILENAME1));
+ commandHandler.handleCommand(command, session);
+ verify(session);
+
+ verifyNumberOfInvocations(commandHandler, 1);
+ verifyTwoDataElements(commandHandler.getInvocation(0), StorCommandHandler.PATHNAME_KEY, FILENAME1,
+ StorCommandHandler.FILE_CONTENTS_KEY, DATA.getBytes());
+ }
+
+ /**
+ * Test the handleCommand() method, when no pathname parameter has been specified
+ */
+ public void testHandleCommand_MissingPathnameParameter() throws Exception {
+ testHandleCommand_InvalidParameters(commandHandler, CommandNames.STOR, EMPTY);
+ }
+
+}
diff --git a/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/StouCommandHandlerTest.java b/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/StouCommandHandlerTest.java
new file mode 100644
index 0000000..84ec063
--- /dev/null
+++ b/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/StouCommandHandlerTest.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.stub.command;
+
+import org.mockftpserver.core.command.Command;
+import org.mockftpserver.core.command.CommandNames;
+import org.mockftpserver.core.command.ReplyCodes;
+import org.mockftpserver.stub.command.StouCommandHandler;
+
+/**
+ * Tests for the StouCommandHandler class
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public final class StouCommandHandlerTest extends AbstractCommandHandlerTest {
+
+ private StouCommandHandler commandHandler;
+
+ /**
+ * Perform initialization before each test
+ *
+ * @see org.mockftpserver.stub.command.AbstractCommandHandlerTest#setUp()
+ */
+ protected void setUp() throws Exception {
+ super.setUp();
+ commandHandler = new StouCommandHandler();
+ commandHandler.setReplyTextBundle(replyTextBundle);
+ }
+
+ /**
+ * Test the handleCommand() method, as well as the getFileContents() and clearFileContents() methods
+ */
+ public void testHandleCommand() throws Exception {
+ final String DATA = "ABC";
+ final String FILENAME = "abc.txt";
+
+ session.sendReply(ReplyCodes.SEND_DATA_INITIAL_OK, replyTextFor(ReplyCodes.SEND_DATA_INITIAL_OK));
+ session.openDataConnection();
+ session.readData();
+ control(session).setReturnValue(DATA.getBytes());
+ session.closeDataConnection();
+ session.sendReply(ReplyCodes.SEND_DATA_FINAL_OK, formattedReplyTextFor("226.WithFilename", FILENAME));
+ replay(session);
+
+ Command command = new Command(CommandNames.STOU, array(FILENAME1));
+ commandHandler.setFilename(FILENAME);
+ commandHandler.handleCommand(command, session);
+ verify(session);
+
+ verifyNumberOfInvocations(commandHandler, 1);
+ verifyOneDataElement(commandHandler.getInvocation(0), StouCommandHandler.FILE_CONTENTS_KEY, DATA.getBytes());
+ }
+
+}
diff --git a/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/StruCommandHandlerTest.java b/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/StruCommandHandlerTest.java
new file mode 100644
index 0000000..2377f1d
--- /dev/null
+++ b/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/StruCommandHandlerTest.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.stub.command;
+
+import org.mockftpserver.core.command.Command;
+import org.mockftpserver.core.command.CommandNames;
+import org.mockftpserver.core.command.ReplyCodes;
+import org.mockftpserver.stub.command.StruCommandHandler;
+
+/**
+ * Tests for the StruCommandHandler class
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public final class StruCommandHandlerTest extends AbstractCommandHandlerTest {
+
+ private static final String CODE1 = "F";
+ private static final String CODE2 = "R";
+
+ private StruCommandHandler commandHandler;
+ private Command command1;
+ private Command command2;
+
+ /**
+ * Test the handleCommand() method
+ */
+ public void testHandleCommand() throws Exception {
+
+ session.sendReply(ReplyCodes.STRU_OK, replyTextFor(ReplyCodes.STRU_OK));
+ session.sendReply(ReplyCodes.STRU_OK, replyTextFor(ReplyCodes.STRU_OK));
+ replay(session);
+
+ commandHandler.handleCommand(command1, session);
+ commandHandler.handleCommand(command2, session);
+ verify(session);
+
+ verifyNumberOfInvocations(commandHandler, 2);
+ verifyOneDataElement(commandHandler.getInvocation(0), StruCommandHandler.FILE_STRUCTURE_KEY, CODE1);
+ verifyOneDataElement(commandHandler.getInvocation(1), StruCommandHandler.FILE_STRUCTURE_KEY, CODE2);
+ }
+
+ /**
+ * Test the handleCommand() method, when no pathname parameter has been specified
+ */
+ public void testHandleCommand_MissingPathnameParameter() throws Exception {
+ testHandleCommand_InvalidParameters(commandHandler, CommandNames.STRU, EMPTY);
+ }
+
+ /**
+ * Perform initialization before each test
+ *
+ * @see org.mockftpserver.stub.command.AbstractCommandHandlerTest#setUp()
+ */
+ protected void setUp() throws Exception {
+ super.setUp();
+ commandHandler = new StruCommandHandler();
+ commandHandler.setReplyTextBundle(replyTextBundle);
+ command1 = new Command(CommandNames.STRU, array(CODE1));
+ command2 = new Command(CommandNames.STRU, array(CODE2));
+ }
+
+}
diff --git a/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/SystCommandHandlerTest.java b/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/SystCommandHandlerTest.java
new file mode 100644
index 0000000..32a7abc
--- /dev/null
+++ b/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/SystCommandHandlerTest.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.stub.command;
+
+import org.apache.log4j.Logger;
+import org.mockftpserver.core.command.Command;
+import org.mockftpserver.core.command.CommandNames;
+import org.mockftpserver.core.command.ReplyCodes;
+import org.mockftpserver.core.util.AssertFailedException;
+import org.mockftpserver.stub.command.SystCommandHandler;
+
+/**
+ * Tests for the SystCommandHandler class
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public final class SystCommandHandlerTest extends AbstractCommandHandlerTest {
+
+ private static final Logger LOG = Logger.getLogger(SystCommandHandlerTest.class);
+
+ private SystCommandHandler commandHandler;
+
+ /**
+ * Test the handleCommand() method
+ */
+ public void testHandleCommand() throws Exception {
+
+ final String SYSTEM_NAME = "UNIX";
+ commandHandler.setSystemName(SYSTEM_NAME);
+
+ session.sendReply(ReplyCodes.SYST_OK, formattedReplyTextFor(ReplyCodes.SYST_OK, "\"" + SYSTEM_NAME + "\""));
+ replay(session);
+
+ final Command COMMAND = new Command(CommandNames.SYST, EMPTY);
+
+ commandHandler.handleCommand(COMMAND, session);
+ verify(session);
+
+ verifyNumberOfInvocations(commandHandler, 1);
+ verifyNoDataElements(commandHandler.getInvocation(0));
+ }
+
+ /**
+ * Test the SetSystemName method, passing in a null
+ */
+ public void testSetSystemName_Null() {
+ try {
+ commandHandler.setSystemName(null);
+ fail("Expected AssertFailedException");
+ }
+ catch (AssertFailedException expected) {
+ LOG.info("Expected: " + expected);
+ }
+ }
+
+ /**
+ * Perform initialization before each test
+ * @see org.mockftpserver.stub.command.AbstractCommandHandlerTest#setUp()
+ */
+ protected void setUp() throws Exception {
+ super.setUp();
+ commandHandler = new SystCommandHandler();
+ commandHandler.setReplyTextBundle(replyTextBundle);
+ }
+
+}
diff --git a/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/TypeCommandHandlerTest.java b/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/TypeCommandHandlerTest.java
new file mode 100644
index 0000000..1be4229
--- /dev/null
+++ b/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/TypeCommandHandlerTest.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.stub.command;
+
+import org.mockftpserver.core.command.Command;
+import org.mockftpserver.core.command.CommandNames;
+import org.mockftpserver.core.command.ReplyCodes;
+import org.mockftpserver.stub.command.TypeCommandHandler;
+
+/**
+ * Tests for the TypeCommandHandler class
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public final class TypeCommandHandlerTest extends AbstractCommandHandlerTest {
+
+ private TypeCommandHandler commandHandler;
+
+ /**
+ * Test the handleCommand() method
+ */
+ public void testHandleCommand() throws Exception {
+ final Command COMMAND1 = new Command("TYPE", array("A"));
+ final Command COMMAND2 = new Command("TYPE", array("B"));
+ final Command COMMAND3 = new Command("TYPE", array("L", "8"));
+
+ session.sendReply(ReplyCodes.TYPE_OK, replyTextFor(ReplyCodes.TYPE_OK));
+ session.sendReply(ReplyCodes.TYPE_OK, replyTextFor(ReplyCodes.TYPE_OK));
+ session.sendReply(ReplyCodes.TYPE_OK, replyTextFor(ReplyCodes.TYPE_OK));
+ replay(session);
+
+ commandHandler.handleCommand(COMMAND1, session);
+ commandHandler.handleCommand(COMMAND2, session);
+ commandHandler.handleCommand(COMMAND3, session);
+ verify(session);
+
+ verifyNumberOfInvocations(commandHandler, 3);
+ verifyOneDataElement(commandHandler.getInvocation(0), TypeCommandHandler.TYPE_INFO_KEY, new String[] {"A", null});
+ verifyOneDataElement(commandHandler.getInvocation(1), TypeCommandHandler.TYPE_INFO_KEY, new String[] {"B", null});
+ verifyOneDataElement(commandHandler.getInvocation(2), TypeCommandHandler.TYPE_INFO_KEY, new String[] {"L", "8"});
+ }
+
+ /**
+ * Test the handleCommand() method, when no type parameter has been specified
+ */
+ public void testHandleCommand_MissingTypeParameter() throws Exception {
+ testHandleCommand_InvalidParameters(commandHandler, CommandNames.TYPE, EMPTY);
+ }
+
+ /**
+ * Perform initialization before each test
+ * @see org.mockftpserver.stub.command.AbstractCommandHandlerTest#setUp()
+ */
+ protected void setUp() throws Exception {
+ super.setUp();
+ commandHandler = new TypeCommandHandler();
+ commandHandler.setReplyTextBundle(replyTextBundle);
+ }
+
+}
diff --git a/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/UserCommandHandlerTest.java b/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/UserCommandHandlerTest.java
new file mode 100644
index 0000000..5cf783e
--- /dev/null
+++ b/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/UserCommandHandlerTest.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.stub.command;
+
+import org.mockftpserver.core.command.Command;
+import org.mockftpserver.core.command.CommandNames;
+import org.mockftpserver.core.command.ReplyCodes;
+import org.mockftpserver.stub.command.UserCommandHandler;
+
+/**
+ * Tests for the UserCommandHandler class
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public final class UserCommandHandlerTest extends AbstractCommandHandlerTest {
+
+ private static final String USERNAME1 = "user1";
+ private static final String USERNAME2 = "user2";
+
+ private UserCommandHandler commandHandler;
+ private Command command1;
+ private Command command2;
+
+ /**
+ * Test the handleCommand() method
+ */
+ public void testHandleCommand() throws Exception {
+
+ session.sendReply(ReplyCodes.USER_NEED_PASSWORD_OK, replyTextFor(ReplyCodes.USER_NEED_PASSWORD_OK));
+ session.sendReply(ReplyCodes.USER_LOGGED_IN_OK, replyTextFor(ReplyCodes.USER_LOGGED_IN_OK));
+ replay(session);
+
+ commandHandler.handleCommand(command1, session);
+ commandHandler.setPasswordRequired(false);
+ commandHandler.handleCommand(command2, session);
+ verify(session);
+
+ verifyNumberOfInvocations(commandHandler, 2);
+ verifyOneDataElement(commandHandler.getInvocation(0), UserCommandHandler.USERNAME_KEY, USERNAME1);
+ verifyOneDataElement(commandHandler.getInvocation(1), UserCommandHandler.USERNAME_KEY, USERNAME2);
+ }
+
+ /**
+ * Test the handleCommand() method, when no username parameter has been specified
+ */
+ public void testHandleCommand_MissingUsernameParameter() throws Exception {
+ testHandleCommand_InvalidParameters(commandHandler, CommandNames.USER, EMPTY);
+ }
+
+ /**
+ * Test the setPasswordRequired() and isPasswordRequired() methods
+ */
+ public void testSetPasswordRequired() {
+ assertTrue("initial state", commandHandler.isPasswordRequired());
+ commandHandler.setPasswordRequired(false);
+ assertFalse("after set false", commandHandler.isPasswordRequired());
+ }
+
+ /**
+ * Perform initialization before each test
+ *
+ * @see org.mockftpserver.stub.command.AbstractCommandHandlerTest#setUp()
+ */
+ protected void setUp() throws Exception {
+ super.setUp();
+ commandHandler = new UserCommandHandler();
+ command1 = new Command(CommandNames.USER, array(USERNAME1));
+ command2 = new Command(CommandNames.USER, array(USERNAME2));
+ commandHandler.setReplyTextBundle(replyTextBundle);
+ }
+
+}
diff --git a/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/_AbstractStubCommandHandlerTest.java b/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/_AbstractStubCommandHandlerTest.java
new file mode 100644
index 0000000..c906905
--- /dev/null
+++ b/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/_AbstractStubCommandHandlerTest.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.stub.command;
+
+import java.util.ListResourceBundle;
+import java.util.ResourceBundle;
+
+import org.apache.log4j.Logger;
+import org.easymock.MockControl;
+import org.mockftpserver.core.command.Command;
+import org.mockftpserver.core.command.InvocationRecord;
+import org.mockftpserver.core.session.Session;
+import org.mockftpserver.core.util.AssertFailedException;
+import org.mockftpserver.test.AbstractTest;
+
+/**
+ * Tests for the AbstractStubCommandHandler class. The class name is prefixed with an underscore
+ * so that it is not filtered out by Maven's Surefire test plugin.
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public final class _AbstractStubCommandHandlerTest extends AbstractTest {
+
+ private static final Logger LOG = Logger.getLogger(_AbstractStubCommandHandlerTest.class);
+ private static final int REPLY_CODE1 = 777;
+ private static final int REPLY_CODE2 = 888;
+ private static final String REPLY_TEXT1 = "reply1 ... abcdef";
+ private static final String REPLY_TEXT2 = "abc {0} def";
+ private static final String REPLY_TEXT2_FORMATTED = "abc 123 def";
+ private static final String MESSAGE_KEY = "key.123";
+ private static final String MESSAGE_TEXT = "message.123";
+ private static final String OVERRIDE_REPLY_TEXT = "overridden reply ... abcdef";
+ private static final Object ARG = "123";
+
+ private AbstractStubCommandHandler commandHandler;
+ private Session session;
+ private ResourceBundle replyTextBundle;
+
+ /**
+ * Test the sendReply(Session) method
+ */
+ public void testSendReply() {
+ session.sendReply(REPLY_CODE1, REPLY_TEXT1);
+ replay(session);
+
+ commandHandler.setReplyCode(REPLY_CODE1);
+ commandHandler.sendReply(session);
+ verify(session);
+ }
+
+ /**
+ * Test the sendReply(Session) method, when the replyText has been set
+ */
+ public void testSendReply_SetReplyText() {
+ session.sendReply(REPLY_CODE1, OVERRIDE_REPLY_TEXT);
+ replay(session);
+
+ commandHandler.setReplyCode(REPLY_CODE1);
+ commandHandler.setReplyText(OVERRIDE_REPLY_TEXT);
+ commandHandler.sendReply(session);
+ verify(session);
+ }
+
+ /**
+ * Test the sendReply(Session) method, when the replyMessageKey has been set
+ */
+ public void testSendReply_SetReplyMessageKey() {
+ session.sendReply(REPLY_CODE1, REPLY_TEXT2);
+ replay(session);
+
+ commandHandler.setReplyCode(REPLY_CODE1);
+ commandHandler.setReplyMessageKey(Integer.toString(REPLY_CODE2));
+ commandHandler.sendReply(session);
+ verify(session);
+ }
+
+ /**
+ * Test the sendReply(Session) method, when the replyCode has not been set
+ */
+ public void testSendReply_ReplyCodeNotSet() {
+ try {
+ commandHandler.sendReply(session);
+ fail("Expected AssertFailedException");
+ }
+ catch (AssertFailedException expected) {
+ LOG.info("Expected: " + expected);
+ }
+ }
+
+ /**
+ * Test the sendReply(Session,Object) method
+ */
+ public void testSendReply_MessageParameter() {
+ session.sendReply(REPLY_CODE2, REPLY_TEXT2);
+ session.sendReply(REPLY_CODE2, REPLY_TEXT2_FORMATTED);
+ replay(session);
+
+ commandHandler.setReplyCode(REPLY_CODE2);
+ commandHandler.sendReply(session);
+ commandHandler.sendReply(session, ARG);
+ verify(session);
+ }
+
+ /**
+ * Test the setReplyCode() method, passing in an invalid value
+ */
+ public void testSetReplyCode_Invalid() {
+ try {
+ commandHandler.setReplyCode(0);
+ fail("Expected AssertFailedException");
+ }
+ catch (AssertFailedException expected) {
+ LOG.info("Expected: " + expected);
+ }
+ }
+
+ //-------------------------------------------------------------------------
+ // Test setup
+ //-------------------------------------------------------------------------
+
+ /**
+ * Perform initialization before each test
+ * @see org.mockftpserver.test.AbstractTest#setUp()
+ */
+ protected void setUp() throws Exception {
+ super.setUp();
+ session = (Session) createMock(Session.class);
+ control(session).setDefaultMatcher(MockControl.ARRAY_MATCHER);
+ commandHandler = new AbstractStubCommandHandler() {
+ public void handleCommand(Command command, Session session, InvocationRecord invocationRecord) throws Exception {
+ }
+ };
+ replyTextBundle = new ListResourceBundle() {
+ protected Object[][] getContents() {
+ return new Object[][] {
+ { Integer.toString(REPLY_CODE1), REPLY_TEXT1 },
+ { Integer.toString(REPLY_CODE2), REPLY_TEXT2 },
+ { MESSAGE_KEY, MESSAGE_TEXT }
+ };
+ }
+ };
+ commandHandler.setReplyTextBundle(replyTextBundle);
+ }
+
+}
diff --git a/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/_AbstractStubDataCommandHandlerTest.java b/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/_AbstractStubDataCommandHandlerTest.java
new file mode 100644
index 0000000..d75632a
--- /dev/null
+++ b/tags/1.2.3/src/test/java/org/mockftpserver/stub/command/_AbstractStubDataCommandHandlerTest.java
@@ -0,0 +1,177 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.stub.command;
+
+import java.util.ListResourceBundle;
+import java.util.ResourceBundle;
+
+import org.apache.log4j.Logger;
+import org.mockftpserver.core.command.Command;
+import org.mockftpserver.core.command.InvocationRecord;
+import org.mockftpserver.core.session.Session;
+import org.mockftpserver.core.util.AssertFailedException;
+import org.mockftpserver.stub.command.AbstractStubDataCommandHandler;
+import org.mockftpserver.test.AbstractTest;
+
+/**
+ * Tests for AbstractStubDataCommandHandler. The class name is prefixed with an underscore
+ * so that it is not filtered out by Maven's Surefire test plugin.
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public final class _AbstractStubDataCommandHandlerTest extends AbstractTest {
+
+ private static final Logger LOG = Logger.getLogger(_AbstractStubDataCommandHandlerTest.class);
+ private static final Command COMMAND = new Command("command", EMPTY);
+ private static final InvocationRecord INVOCATION_RECORD = new InvocationRecord(COMMAND, DEFAULT_HOST);
+
+ private static final String REPLY_TEXT150 = "reply 150 ... abcdef";
+ private static final String REPLY_TEXT226 = "reply 226 ... abcdef";
+ private static final String REPLY_TEXT222 = "reply 222 ... abcdef";
+ private static final String REPLY_TEXT333 = "reply 333 ... abcdef";
+ private static final String REPLY_TEXT444 = "reply 444 ... abcdef";
+
+ private Session session;
+ private ResourceBundle replyTextBundle;
+ private AbstractStubDataCommandHandler commandHandler;
+
+ /**
+ * Test the handleCommand() method
+ */
+ public void testHandleCommand() throws Exception {
+
+ session.sendReply(150, REPLY_TEXT150);
+ session.openDataConnection();
+ session.sendReply(222, REPLY_TEXT222);
+ session.sendReply(333, REPLY_TEXT333);
+ session.sendReply(444, REPLY_TEXT444);
+ session.closeDataConnection();
+ session.sendReply(226, REPLY_TEXT226);
+ replay(session);
+
+ // Define CommandHandler test subclass
+ commandHandler = new AbstractStubDataCommandHandler() {
+ protected void beforeProcessData(Command c, Session s, InvocationRecord ir) {
+ verifyParameters(c, s, ir);
+ // Send unique reply code so that we can verify proper method invocation and ordering
+ session.sendReply(222, REPLY_TEXT222);
+ }
+
+ protected void processData(Command c, Session s, InvocationRecord ir) {
+ verifyParameters(c, s, ir);
+ // Send unique reply code so that we can verify proper method invocation and ordering
+ session.sendReply(333, REPLY_TEXT333);
+ }
+
+ protected void afterProcessData(Command c, Session s, InvocationRecord ir) {
+ verifyParameters(c, s, ir);
+ // Send unique reply code so that we can verify proper method invocation and ordering
+ session.sendReply(444, REPLY_TEXT444);
+ }
+
+ private void verifyParameters(Command c, Session s, InvocationRecord ir) {
+ assertSame("command", COMMAND, c);
+ assertSame("session", session, s);
+ assertSame("invocationRecord", INVOCATION_RECORD, ir);
+ }
+ };
+
+ commandHandler.setReplyTextBundle(replyTextBundle);
+ commandHandler.handleCommand(COMMAND, session, INVOCATION_RECORD);
+
+ verify(session);
+ }
+
+ /**
+ * Test the handleCommand() method, overriding the initial reply code and text
+ */
+ public void testHandleCommand_OverrideInitialReplyCodeAndText() throws Exception {
+
+ final int OVERRIDE_REPLY_CODE = 333;
+ final String OVERRIDE_REPLY_TEXT = "reply text";
+
+ session.sendReply(OVERRIDE_REPLY_CODE, OVERRIDE_REPLY_TEXT);
+ session.openDataConnection();
+ session.closeDataConnection();
+ session.sendReply(226, REPLY_TEXT226);
+ replay(session);
+
+ commandHandler.setPreliminaryReplyCode(OVERRIDE_REPLY_CODE);
+ commandHandler.setPreliminaryReplyText(OVERRIDE_REPLY_TEXT);
+ commandHandler.setReplyTextBundle(replyTextBundle);
+ commandHandler.handleCommand(COMMAND, session, INVOCATION_RECORD);
+
+ verify(session);
+ }
+
+ /**
+ * Test the setPreliminaryReplyCode() method, passing in an invalid value
+ */
+ public void testSetPreliminaryReplyCode_Invalid() {
+ try {
+ commandHandler.setPreliminaryReplyCode(0);
+ fail("Expected AssertFailedException");
+ }
+ catch (AssertFailedException expected) {
+ LOG.info("Expected: " + expected);
+ }
+ }
+
+ /**
+ * Test the setFinalReplyCode() method, passing in an invalid value
+ */
+ public void testSetFinalReplyCode_Invalid() {
+ try {
+ commandHandler.setFinalReplyCode(0);
+ fail("Expected AssertFailedException");
+ }
+ catch (AssertFailedException expected) {
+ LOG.info("Expected: " + expected);
+ }
+ }
+
+ //-------------------------------------------------------------------------
+ // Test setup
+ //-------------------------------------------------------------------------
+
+ /**
+ * Perform initialization before each test
+ *
+ * @see org.mockftpserver.test.AbstractTest#setUp()
+ */
+ protected void setUp() throws Exception {
+ super.setUp();
+ session = (Session) createMock(Session.class);
+ replyTextBundle = new ListResourceBundle() {
+ protected Object[][] getContents() {
+ return new Object[][] {
+ { Integer.toString(150), REPLY_TEXT150 },
+ { Integer.toString(222), REPLY_TEXT222 },
+ { Integer.toString(226), REPLY_TEXT226 },
+ { Integer.toString(333), REPLY_TEXT333 },
+ { Integer.toString(444), REPLY_TEXT444 },
+ };
+ }
+ };
+ commandHandler = new AbstractStubDataCommandHandler() {
+ protected void processData(Command c, Session s, InvocationRecord ir) {
+ }
+ };
+ }
+
+}
diff --git a/tags/1.2.3/src/test/java/org/mockftpserver/stub/example/FtpWorkingDirectory.java b/tags/1.2.3/src/test/java/org/mockftpserver/stub/example/FtpWorkingDirectory.java
new file mode 100644
index 0000000..360358a
--- /dev/null
+++ b/tags/1.2.3/src/test/java/org/mockftpserver/stub/example/FtpWorkingDirectory.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.stub.example;
+
+import java.io.IOException;
+import java.net.SocketException;
+
+import org.apache.commons.net.ftp.FTPClient;
+
+/**
+ * Simple FTP client code example.
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public class FtpWorkingDirectory {
+
+ private String server;
+ private int port;
+
+ /**
+ * Return the current working directory for the FTP account on the server
+ * @return the current working directory
+ * @throws SocketException
+ * @throws IOException
+ */
+ public String getWorkingDirectory() throws SocketException, IOException {
+ FTPClient ftpClient = new FTPClient();
+ ftpClient.connect(server, port);
+ return ftpClient.printWorkingDirectory();
+ }
+
+ /**
+ * Set the hostname of the FTP server
+ * @param server - the hostname of the FTP server
+ */
+ public void setServer(String server) {
+ this.server = server;
+ }
+
+ /**
+ * Set the port number for the FTP server
+ * @param port - the port number
+ */
+ public void setPort(int port) {
+ this.port = port;
+ }
+
+}
diff --git a/tags/1.2.3/src/test/java/org/mockftpserver/stub/example/FtpWorkingDirectoryTest.java b/tags/1.2.3/src/test/java/org/mockftpserver/stub/example/FtpWorkingDirectoryTest.java
new file mode 100644
index 0000000..7edd3d0
--- /dev/null
+++ b/tags/1.2.3/src/test/java/org/mockftpserver/stub/example/FtpWorkingDirectoryTest.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.stub.example;
+
+import org.mockftpserver.core.command.CommandNames;
+import org.mockftpserver.stub.StubFtpServer;
+import org.mockftpserver.stub.command.PwdCommandHandler;
+import org.mockftpserver.test.AbstractTest;
+import org.mockftpserver.test.IntegrationTest;
+
+/**
+ * Example test using StubFtpServer, with programmatic configuration.
+ */
+public class FtpWorkingDirectoryTest extends AbstractTest implements IntegrationTest {
+
+ private static final int PORT = 9981;
+ private FtpWorkingDirectory ftpWorkingDirectory;
+ private StubFtpServer stubFtpServer;
+
+ /**
+ * Test FtpWorkingDirectory getWorkingDirectory() method
+ */
+ public void testGetWorkingDirectory() throws Exception {
+
+ // Replace the existing (default) CommandHandler; customize returned directory pathname
+ final String DIR = "some/dir";
+ PwdCommandHandler pwdCommandHandler = new PwdCommandHandler();
+ pwdCommandHandler.setDirectory(DIR);
+ stubFtpServer.setCommandHandler(CommandNames.PWD, pwdCommandHandler);
+
+ stubFtpServer.start();
+
+ String workingDir = ftpWorkingDirectory.getWorkingDirectory();
+
+ assertEquals("workingDirectory", DIR, workingDir);
+ }
+
+ /**
+ * @see org.mockftpserver.test.AbstractTest#setUp()
+ */
+ protected void setUp() throws Exception {
+ super.setUp();
+ ftpWorkingDirectory = new FtpWorkingDirectory();
+ ftpWorkingDirectory.setPort(PORT);
+ stubFtpServer = new StubFtpServer();
+ stubFtpServer.setServerControlPort(PORT);
+ }
+
+ /**
+ * @see org.mockftpserver.test.AbstractTest#tearDown()
+ */
+ protected void tearDown() throws Exception {
+ super.tearDown();
+ stubFtpServer.stop();
+ }
+
+}
diff --git a/tags/1.2.3/src/test/java/org/mockftpserver/stub/example/RemoteFile.java b/tags/1.2.3/src/test/java/org/mockftpserver/stub/example/RemoteFile.java
new file mode 100644
index 0000000..0b8f85f
--- /dev/null
+++ b/tags/1.2.3/src/test/java/org/mockftpserver/stub/example/RemoteFile.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.stub.example;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.net.SocketException;
+
+import org.apache.commons.net.ftp.FTPClient;
+
+/**
+ * Simple FTP client code example.
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public class RemoteFile {
+
+ private String server;
+ private int port;
+
+ public String readFile(String filename) throws SocketException, IOException {
+
+ FTPClient ftpClient = new FTPClient();
+ ftpClient.connect(server, port);
+
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ boolean success = ftpClient.retrieveFile(filename, outputStream);
+ ftpClient.disconnect();
+
+ if (!success) {
+ throw new IOException("Retrieve file failed: " + filename);
+ }
+ return outputStream.toString();
+ }
+
+ /**
+ * Set the hostname of the FTP server
+ * @param server - the hostname of the FTP server
+ */
+ public void setServer(String server) {
+ this.server = server;
+ }
+
+ /**
+ * Set the port number for the FTP server
+ * @param port - the port number
+ */
+ public void setPort(int port) {
+ this.port = port;
+ }
+
+ // Other methods ...
+}
diff --git a/tags/1.2.3/src/test/java/org/mockftpserver/stub/example/RemoteFileTest.java b/tags/1.2.3/src/test/java/org/mockftpserver/stub/example/RemoteFileTest.java
new file mode 100644
index 0000000..0e87742
--- /dev/null
+++ b/tags/1.2.3/src/test/java/org/mockftpserver/stub/example/RemoteFileTest.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.stub.example;
+
+import java.io.IOException;
+
+import org.mockftpserver.core.command.CommandNames;
+import org.mockftpserver.core.command.InvocationRecord;
+import org.mockftpserver.stub.StubFtpServer;
+import org.mockftpserver.stub.command.RetrCommandHandler;
+import org.mockftpserver.test.AbstractTest;
+import org.mockftpserver.test.IntegrationTest;
+
+/**
+ * Example test using StubFtpServer, with programmatic configuration.
+ */
+public class RemoteFileTest extends AbstractTest implements IntegrationTest {
+
+ private static final int PORT = 9981;
+ 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(CommandNames.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.setFinalReplyCode(550);
+ stubFtpServer.setCommandHandler(CommandNames.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");
+ remoteFile.setPort(PORT);
+ stubFtpServer = new StubFtpServer();
+ stubFtpServer.setServerControlPort(PORT);
+ }
+
+ /**
+ * @see org.mockftpserver.test.AbstractTest#tearDown()
+ */
+ protected void tearDown() throws Exception {
+ super.tearDown();
+ stubFtpServer.stop();
+ }
+
+}
diff --git a/tags/1.2.3/src/test/java/org/mockftpserver/stub/example/SpringConfigurationTest.java b/tags/1.2.3/src/test/java/org/mockftpserver/stub/example/SpringConfigurationTest.java
new file mode 100644
index 0000000..8c52113
--- /dev/null
+++ b/tags/1.2.3/src/test/java/org/mockftpserver/stub/example/SpringConfigurationTest.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.stub.example;
+
+import java.io.ByteArrayOutputStream;
+
+import org.apache.commons.net.ftp.FTPClient;
+import org.apache.commons.net.ftp.FTPFile;
+import org.apache.log4j.Logger;
+import org.mockftpserver.stub.StubFtpServer;
+import org.mockftpserver.test.AbstractTest;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+
+/**
+ * Example test for StubFtpServer, using the Spring Framework ({@link http://www.springframework.org/})
+ * for configuration.
+ */
+public class SpringConfigurationTest extends AbstractTest {
+
+ private static final Logger LOG = Logger.getLogger(SpringConfigurationTest.class);
+ private static final String SERVER = "localhost";
+ private static final int PORT = 9981;
+
+ private StubFtpServer stubFtpServer;
+ private FTPClient ftpClient;
+
+ /**
+ * Test starting the StubFtpServer configured within the example Spring configuration file
+ */
+ public void testStubFtpServer() throws Exception {
+ stubFtpServer.start();
+
+ ftpClient.connect(SERVER, PORT);
+
+ // PWD
+ String dir = ftpClient.printWorkingDirectory();
+ assertEquals("PWD", "foo/bar", dir);
+
+ // LIST
+ FTPFile[] files = ftpClient.listFiles();
+ LOG.info("FTPFile[0]=" + files[0]);
+ LOG.info("FTPFile[1]=" + files[1]);
+ assertEquals("number of files from LIST", 2, files.length);
+
+ // DELE
+ assertFalse("DELE", ftpClient.deleteFile("AnyFile.txt"));
+
+ // RETR
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ assertTrue(ftpClient.retrieveFile("SomeFile.txt", outputStream));
+ LOG.info("File contents=[" + outputStream.toString() + "]");
+ }
+
+ /**
+ * @see org.mockftpserver.test.AbstractTest#setUp()
+ */
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ ApplicationContext context = new ClassPathXmlApplicationContext("stubftpserver-beans.xml");
+ stubFtpServer = (StubFtpServer) context.getBean("stubFtpServer");
+
+ ftpClient = new FTPClient();
+ }
+
+ /**
+ * @see org.mockftpserver.test.AbstractTest#tearDown()
+ */
+ protected void tearDown() throws Exception {
+ super.tearDown();
+ stubFtpServer.stop();
+ }
+
+}
diff --git a/tags/1.2.3/src/test/java/org/mockftpserver/test/AbstractDirectoryTestSuite.java b/tags/1.2.3/src/test/java/org/mockftpserver/test/AbstractDirectoryTestSuite.java
new file mode 100644
index 0000000..04a22a4
--- /dev/null
+++ b/tags/1.2.3/src/test/java/org/mockftpserver/test/AbstractDirectoryTestSuite.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.test;
+
+import junit.framework.Test;
+import junitx.util.DirectorySuiteBuilder;
+import junitx.util.SimpleTestFilter;
+
+/**
+ * Abstract superclass for test suites that recursively process a directory of
+ * Java class files and run all matching unit test classes. Subclasses must
+ * implement the {@link #getTestClassesDirectory()} method to specify the root
+ * directory for the Java class files. Only class names that end in 'Test' are
+ * included. Furthermore, only concrete classes (non-abstract and non-interface)
+ * classes are included. Subclasses can perform additional filtering by overriding
+ * the {@link #isClassIncluded(Class)} method to filter based on the class.
+ * <p>
+ * A typical pattern is to define application-specific marker interfaces, such as
+ * 'IntegrationTest' or 'LongRunningTest', and have test classes implement the
+ * appropriate interface(s). A subclass that runs unit tests, for instance, can
+ * then exclude such classes, while another subclass for integration tests can
+ * include only those classes that implement 'IntegrationTest'.
+ * <p>
+ * Note: This class requires the JUnit-addons jar.
+ *
+ * @see http://junit-addons.sourceforge.net/
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public abstract class AbstractDirectoryTestSuite extends AbstractTestSuite {
+
+ private static final String TEST_CLASSES_DIR_PROPERTY = "test.classes.dir";
+
+ /**
+ * Constructor
+ */
+ protected AbstractDirectoryTestSuite() {
+
+ // Handle (wrap) any Exception to allow subclasses to use default constructor
+ try {
+ Test suite = buildAllTestsSuite();
+ addTest(suite);
+ }
+ catch(Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ //-------------------------------------------------------------------------
+ // Abstract and Overridable Methods
+ //-------------------------------------------------------------------------
+
+ /**
+ * Return the path of the root class file directory
+ */
+ protected abstract String getTestClassesDirectory();
+
+ /**
+ * Return true if the specified class should be included in the test suite.
+ * This method will only be invoked for concrete classes whose names end in 'Test'.
+ * @param theClass - the class
+ * @return true if the specified class should be included in the test suite
+ */
+ protected boolean isClassIncluded(Class theClass) {
+ // Default to including all classes
+ return true;
+ }
+
+ /**
+ * Return true if all ignored tests should be listed to System.out. Subclasses
+ * can override this and return true to list out all ignored test classes.
+ * @return true if all ignored tests should be listed to System.out
+ */
+ protected boolean listIgnoredTests() {
+ return false;
+ }
+
+ //-------------------------------------------------------------------------
+ // Private Helper Methods
+ //-------------------------------------------------------------------------
+
+ /**
+ * Build the TestSuite containing the tests to be run
+ * @return the test suite as a Test
+ */
+ private Test buildAllTestsSuite() throws Exception {
+
+ DirectorySuiteBuilder builder = new DirectorySuiteBuilder();
+ builder.setFilter(new SimpleTestFilter() {
+
+ public boolean include(Class theClass) {
+ boolean superInclude = super.include(theClass);
+ boolean include = isClassIncluded(theClass);
+ if (superInclude && !include && listIgnoredTests()) {
+ System.out.println("IGNORED [" + theClass.getName() + "]");
+ }
+ return superInclude && include;
+ }
+ });
+
+ return builder.suite(getDirectory());
+ }
+
+
+ /**
+ * Return the path of the root class file directory. Use the "test.classes.dir"
+ * system property value if it has been set, otherwise use the value returned
+ * from the getDefaultDirectory() method.
+ *
+ * @return the path of the root class file directory
+ */
+ private String getDirectory() {
+
+ String sysProp = System.getProperty(TEST_CLASSES_DIR_PROPERTY);
+ return (sysProp == null) ? getTestClassesDirectory() : sysProp;
+ }
+
+}
diff --git a/tags/1.2.3/src/test/java/org/mockftpserver/test/AbstractTest.java b/tags/1.2.3/src/test/java/org/mockftpserver/test/AbstractTest.java
new file mode 100644
index 0000000..6fb1620
--- /dev/null
+++ b/tags/1.2.3/src/test/java/org/mockftpserver/test/AbstractTest.java
@@ -0,0 +1,355 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.test;
+
+import java.io.File;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import junit.framework.TestCase;
+
+import org.apache.log4j.Logger;
+import org.easymock.MockControl;
+import org.mockftpserver.core.MockFtpServerException;
+import org.mockftpserver.core.util.Assert;
+import org.mockftpserver.core.util.AssertFailedException;
+
+/**
+ * Abstract superclass for all project test classes
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public abstract class AbstractTest extends TestCase {
+
+ private static final Logger LOG = Logger.getLogger(AbstractTest.class);
+ protected static final List EMPTY_LIST = Collections.EMPTY_LIST;
+ protected static final String[] EMPTY = new String[0];
+ protected static final InetAddress DEFAULT_HOST = inetAddress(null);
+
+ /**
+ * Constructor
+ */
+ public AbstractTest() {
+ super();
+ }
+
+ //-------------------------------------------------------------------------
+ // Manage EasyMock Control objects under the covers, and provide a syntax
+ // somewhat similar to EasyMock 2.2 for createMock, verify and replay.
+ //-------------------------------------------------------------------------
+
+ private Map mocks = new HashMap();
+
+ /**
+ * Create a new mock for the specified interface. Keep track of the associated control object
+ * under the covers to support the associated method.
+ * @param interfaceToMock - the Class of the interface to be mocked
+ * @return the new mock
+ */
+ protected Object createMock(Class interfaceToMock) {
+ MockControl control = MockControl.createControl(interfaceToMock);
+ Object mock = control.getMock();
+ mocks.put(mock, control);
+ return mock;
+ }
+
+ /**
+ * Put the mock object into replay mode
+ * @param mock - the mock to set in replay mode
+ * @throws AssertFailedException - if mock is null
+ * @throws AssertFailedException - if mock is not a mock object created using {@link #createMock(Class)}
+ */
+ protected void replay(Object mock) {
+ control(mock).replay();
+ }
+
+ /**
+ * Put all mocks created with createMock() into replay mode.
+ */
+ protected void replayAll() {
+ for (Iterator iter = mocks.keySet().iterator(); iter.hasNext();) {
+ Object mock = iter.next();
+ replay(mock);
+ }
+ }
+ /**
+ * Verify the mock object
+ * @param mock - the mock to verify
+ * @throws AssertFailedException - if mock is null
+ * @throws AssertFailedException - if mock is not a mock object created using {@link #createMock(Class)}
+ */
+ protected void verify(Object mock) {
+ control(mock).verify();
+ }
+
+ /**
+ * Verify all mocks created with createMock() into replay mode.
+ */
+ protected void verifyAll() {
+ for (Iterator iter = mocks.keySet().iterator(); iter.hasNext();) {
+ Object mock = iter.next();
+ verify(mock);
+ }
+ }
+
+ /**
+ * Return the mock control associated with the mock
+ * @param mock - the mock
+ * @return the associated MockControl
+ * @throws AssertFailedException - if mock is null
+ * @throws AssertFailedException - if mock is not a mock object created using {@link #createMock(Class)}
+ */
+ protected MockControl control(Object mock) {
+ Assert.notNull(mock, "mock");
+ MockControl control = (MockControl) mocks.get(mock);
+ Assert.notNull(control, "control");
+ return control;
+ }
+
+ //-------------------------------------------------------------------------
+ // Other Helper Methods
+ //-------------------------------------------------------------------------
+
+ /**
+ * Assert that the two objects are not equal
+ * @param object1 - the first object
+ * @param object2 - the second object
+ */
+ protected void assertNotEquals(String message, Object object1, Object object2) {
+ assertFalse(message, object1.equals(object2));
+ }
+
+ /**
+ * Assert that the two byte arrays have the same length and content
+ * @param array1 - the first array
+ * @param array2 - the second array
+ */
+ protected void assertEquals(String message, byte[] array1, byte[] array2) {
+ assertTrue("Arrays not equal: " + message, Arrays.equals(array1, array2));
+ }
+
+ /**
+ * Assert that the two Object arrays have the same length and content
+ * @param array1 - the first array
+ * @param array2 - the second array
+ */
+ protected void assertEquals(String message, Object[] array1, Object[] array2) {
+ assertTrue("Arrays not equal: " + message, Arrays.equals(array1, array2));
+ }
+
+ /**
+ * Create and return a one-element Object[] containing the specified Object
+ * @param o - the object
+ * @return the Object array, of length 1, containing o
+ */
+ protected static Object[] objArray(Object o) {
+ return new Object[] { o };
+ }
+
+ /**
+ * Create and return a one-element String[] containing the specified String
+ * @param s - the String
+ * @return the String array, of length 1, containing s
+ */
+ protected static String[] array(String s) {
+ return new String[] { s };
+ }
+
+ /**
+ * Create and return a two-element String[] containing the specified Strings
+ * @param s1 - the first String
+ * @param s2 - the second String
+ * @return the String array, of length 2, containing s1 and s2
+ */
+ protected static String[] array(String s1, String s2) {
+ return new String[] { s1, s2 };
+ }
+
+ /**
+ * Create a new InetAddress from the specified host String, using the
+ * {@link InetAddress#getByName(String)} method, wrapping any checked
+ * exception within a unchecked MockFtpServerException.
+ * @param host
+ * @return an InetAddress for the specified host
+ *
+ * @throws MockFtpServerException - if an UnknownHostException is thrown
+ */
+ protected static InetAddress inetAddress(String host) {
+ try {
+ return InetAddress.getByName(host);
+ }
+ catch (UnknownHostException e) {
+ throw new MockFtpServerException(e);
+ }
+ }
+
+ /**
+ * Create and return a List containing the Objects passed as arguments to this method
+ * @param e1- the first element to add
+ * @param e2- the second element to add
+ * @return the List containing the specified elements
+ */
+ protected static List list(Object e1, Object e2) {
+ List list = new ArrayList();
+ list.add(e1);
+ list.add(e2);
+ return list;
+ }
+
+ /**
+ * Create and return a List containing the single Object passed as an argument to this method
+ * @param element- the element to add
+ * @return the List containing the specified element
+ */
+ protected static List list(Object element) {
+ return Collections.singletonList(element);
+ }
+
+ /**
+ * Create and return a Set containing the Objects passed as arguments to this method
+ * @param e1 - the first element to add
+ * @param e2 - the second element to add
+ * @return the Set containing the specified elements
+ */
+ protected static Set set(Object e1, Object e2) {
+ Set set = new HashSet();
+ set.add(e1);
+ set.add(e2);
+ return set;
+ }
+
+ /**
+ * Create and return a Set containing the Objects passed as arguments to this method
+ * @param e1 - the first element to add
+ * @param e2 - the second element to add
+ * @param e3 - the third element to add
+ * @return the Set containing the specified elements
+ */
+ protected static Set set(Object e1, Object e2, Object e3) {
+ Set set = set(e1, e2);
+ set.add(e3);
+ return set;
+ }
+
+ /**
+ * Override the default test run behavior to write out the current test name
+ * and handle Errors and Exceptions in a standard way.
+ *
+ * @see junit.framework.TestCase#runBare()
+ */
+ public void runBare() throws Throwable {
+
+ LoggingUtil loggingUtil = null;
+ try {
+ loggingUtil = LoggingUtil.getTestCaseLogger(this);
+ loggingUtil.logStartOfTest();
+ super.runBare();
+ }
+ catch (Exception e) {
+ handleException(e);
+ }
+ catch (Error e) {
+ handleError(e);
+ }
+ finally {
+ if (loggingUtil != null) {
+ loggingUtil.logEndOfTest();
+ }
+ }
+ }
+
+ /**
+ * Setup before each test.
+ */
+ protected void setUp() throws Exception {
+ super.setUp();
+ }
+
+ /**
+ * Cleanup after each test.
+ */
+ protected void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ //-----------------------------------------------------------
+ // Private Internal Methods
+ //-----------------------------------------------------------
+
+ /**
+ * Handle an exception
+ * @param e the Exception
+ * @throws Exception
+ */
+ private void handleException(Exception e) throws Exception {
+
+ LOG.error("EXCEPTION: ", e);
+ throw e;
+ }
+
+ /**
+ * Handle an Error
+ * @param e the Error
+ * @throws Exception
+ */
+ private void handleError(Error e) throws Exception {
+ LOG.error("ERROR: ", e);
+ throw e;
+ }
+
+ //-------------------------------------------------------------------------
+ // Helper methods
+ //-------------------------------------------------------------------------
+
+ /**
+ * Delete the named file if it exists
+ *
+ * @param filename - the full pathname of the file
+ */
+ protected void deleteFile(String filename) {
+ File keyFile = new File(filename);
+ boolean deleted = keyFile.delete();
+ LOG.info("Deleted [" + filename + "]: " + deleted);
+ }
+
+
+
+ //-------------------------------------------------------------------------
+ // Common validation helper methods
+ //-------------------------------------------------------------------------
+
+ /**
+ * Verify that the named file exists
+ *
+ * @param filename - the full pathname of the file
+ */
+ protected void verifyFileExists(String filename) {
+ File keyFile = new File(filename);
+ assertTrue("File does not exist [" + filename + "]", keyFile.exists());
+ }
+
+}
diff --git a/tags/1.2.3/src/test/java/org/mockftpserver/test/AbstractTestSuite.java b/tags/1.2.3/src/test/java/org/mockftpserver/test/AbstractTestSuite.java
new file mode 100644
index 0000000..6a3af56
--- /dev/null
+++ b/tags/1.2.3/src/test/java/org/mockftpserver/test/AbstractTestSuite.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.test;
+
+import junit.framework.TestResult;
+import junit.framework.TestSuite;
+
+/**
+ * The absttract superclass for TestSuites that allow registering
+ * TestSuiteListeners to add customized behavior before and after the
+ * test suite is executed.
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public class AbstractTestSuite extends TestSuite {
+
+ /**
+ * Constructor that takes the test name
+ * @param testName Name of test
+ */
+ public AbstractTestSuite() {}
+
+ //-----------------------------------------------------------
+ // Overridden TestSuite Methods
+ //-----------------------------------------------------------
+
+ /**
+ * Override this method to insert hooks for before and after the test suite run
+ * @see junit.framework.Test#run(junit.framework.TestResult)
+ */
+ public void run(TestResult arg0) {
+
+ LoggingUtil loggingUtil = LoggingUtil.getTestSuiteLogger(this);
+ loggingUtil.logStartOfTest();
+
+ // Run this test suite
+ super.run(arg0);
+
+ loggingUtil.logEndOfTest();
+ }
+
+}
diff --git a/tags/1.2.3/src/test/java/org/mockftpserver/test/IntegrationTest.java b/tags/1.2.3/src/test/java/org/mockftpserver/test/IntegrationTest.java
new file mode 100644
index 0000000..0f6e3ae
--- /dev/null
+++ b/tags/1.2.3/src/test/java/org/mockftpserver/test/IntegrationTest.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.test;
+
+/**
+ * Marker interface for integration test
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public interface IntegrationTest {
+
+}
diff --git a/tags/1.2.3/src/test/java/org/mockftpserver/test/LoggingUtil.java b/tags/1.2.3/src/test/java/org/mockftpserver/test/LoggingUtil.java
new file mode 100644
index 0000000..d7347ca
--- /dev/null
+++ b/tags/1.2.3/src/test/java/org/mockftpserver/test/LoggingUtil.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.test;
+
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+/**
+ * Provides facilities to log the start and end of a test run.
+ *
+ * May want to refactor this and create two subclasses: TestCaseLogger
+ * and TestSuiteLogger.
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public final class LoggingUtil {
+
+ private static final String TEST_CASE_SEPARATOR = "---------------";
+ private static final String TEST_SUITE_SEPARATOR = "####################";
+
+ private String testTitle;
+ private String separator;
+ private long startTime;
+
+
+ //-------------------------------------------------------------------------
+ // General-purpose API to log messages
+ //-------------------------------------------------------------------------
+
+ /**
+ * Log the specified message from the caller object.
+ * @param caller the calling object
+ * @param message the message to log
+ */
+ public static void log(Object caller, Object message) {
+
+ String classNameNoPackage = getClassName(caller);
+ String messageStr = (message==null) ? "null" : message.toString();
+ String formattedMessage = "[" + classNameNoPackage + "] " + messageStr;
+ writeLogMessage(formattedMessage);
+ }
+
+
+ //-------------------------------------------------------------------------
+ // Factory Methods to get instance for TestCase or TestSuite
+ //-------------------------------------------------------------------------
+
+ /**
+ * Return a LoggingUtil instance suitable for logging TestCase start and end
+ * @param testCase the TestCase
+ * @return a LoggingUtil
+ */
+ public static LoggingUtil getTestCaseLogger(TestCase testCase) {
+
+ String title = getClassName(testCase) + "." + testCase.getName();
+ return new LoggingUtil(title, TEST_CASE_SEPARATOR);
+ }
+
+
+ /**
+ * Return a LoggingUtil instance suitable for logging TestSuite start and end
+ * @param testSuite the TestSuite
+ * @return a LoggingUtil
+ */
+ public static LoggingUtil getTestSuiteLogger(TestSuite testCase) {
+
+ String title = "SUITE " + getClassName(testCase);
+ return new LoggingUtil(title, TEST_SUITE_SEPARATOR);
+ }
+
+
+ /**
+ * Constructor. Private to force access through the factory method(s)
+ */
+ private LoggingUtil(String title, String separator) {
+ this.startTime = System.currentTimeMillis();
+ this.testTitle = title;
+ this.separator = separator;
+ }
+
+
+ /**
+ * Write out the the name of the test class and test name to the log
+ */
+ public void logStartOfTest() {
+
+ writeLogMessage(separator + " [ START: " + testTitle + " ] " + separator);
+ }
+
+
+ /**
+ * Write out the the name of the test class and test name to the log
+ */
+ public void logEndOfTest() {
+
+ long elapsedTime = System.currentTimeMillis() - startTime;
+ writeLogMessage(separator + " [ END: "
+ + testTitle
+ + " Time=" + elapsedTime
+ + "ms ] "+ separator + "\n");
+ }
+
+
+ /**
+ * Return the name of the class for the specified object, stripping off the package name
+ * @return the name of the class, stripping off the package name
+ */
+ private static String getClassName(Object object) {
+
+ // If it's already a class, then use as is
+ Class theClass = (object instanceof Class) ? ((Class)object) : object.getClass();
+ String className = theClass.getName();
+
+ int index = className.lastIndexOf(".");
+ if (index != -1) {
+ className = className.substring(index+1);
+ }
+ return className;
+ }
+
+
+ /**
+ * Write the specified message out to the log
+ * @param message the message to write
+ */
+ private static void writeLogMessage(String message) {
+ // Don't want to use Trace -- it requires initialization of the system configuration
+ //Trace.trace(message);
+ System.out.println(message);
+ }
+
+}
diff --git a/tags/1.2.3/src/test/java/org/mockftpserver/test/MockFtpServerTestSuite.java b/tags/1.2.3/src/test/java/org/mockftpserver/test/MockFtpServerTestSuite.java
new file mode 100644
index 0000000..6c5651d
--- /dev/null
+++ b/tags/1.2.3/src/test/java/org/mockftpserver/test/MockFtpServerTestSuite.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mockftpserver.test;
+
+import junit.framework.Test;
+
+/**
+ * Unit test suite for the StubFtpServer application
+ *
+ * @version $Revision$ - $Date$
+ *
+ * @author Chris Mair
+ */
+public class MockFtpServerTestSuite extends AbstractDirectoryTestSuite {
+
+ private static final String CLASSES_DIR = "target/test-classes";
+
+ /**
+ * @see org.mockftpserver.test.AbstractDirectoryTestSuite#getTestClassesDirectory()
+ */
+ protected String getTestClassesDirectory() {
+ return CLASSES_DIR;
+ }
+
+
+ /**
+ * Return the TestSuite containing the tests to be run
+ * @return the test suite as a Test
+ */
+ public static Test suite() {
+ return new MockFtpServerTestSuite();
+ }
+
+ /**
+ * Run the test suite
+ */
+ public static void main(String[] args) throws Exception {
+ junit.textui.TestRunner.run(suite());
+ }
+
+} \ No newline at end of file
diff --git a/tags/1.2.3/src/test/java/org/mockftpserver/test/PortTestUtil.java b/tags/1.2.3/src/test/java/org/mockftpserver/test/PortTestUtil.java
new file mode 100644
index 0000000..a0269dd
--- /dev/null
+++ b/tags/1.2.3/src/test/java/org/mockftpserver/test/PortTestUtil.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2008 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.mockftpserver.test;
+
+/**
+ * Contains static test utility method to determine FTP server port number to use for tests
+ *
+ * @version $Revision: $ - $Date: $
+ *
+ * @author Chris Mair
+ */
+public final class PortTestUtil {
+
+ private static final int DEFAULT_SERVER_CONTROL_PORT = 21;
+ private static final String FTP_SERVER_PORT_PROPERTY = "ftp.server.port";
+
+ /**
+ * Return the port number to use for the FTP server control port. If the "ftp.server.port"
+ * system property is defined, then use that value (converted to an integer), otherwise
+ * return the default port number of 21.
+ *
+ * @return the port number to use for the FTP server control port
+ */
+ public static int getFtpServerControlPort() {
+ String systemProperty = System.getProperty(FTP_SERVER_PORT_PROPERTY);
+ return (systemProperty == null) ? DEFAULT_SERVER_CONTROL_PORT : Integer.parseInt(systemProperty);
+ }
+
+}
diff --git a/tags/1.2.3/src/test/resources/Sample.data b/tags/1.2.3/src/test/resources/Sample.data
new file mode 100644
index 0000000..628a3cd
--- /dev/null
+++ b/tags/1.2.3/src/test/resources/Sample.data
@@ -0,0 +1 @@
+ \ No newline at end of file
diff --git a/tags/1.2.3/src/test/resources/SampleReplyText.properties b/tags/1.2.3/src/test/resources/SampleReplyText.properties
new file mode 100644
index 0000000..7a19a33
--- /dev/null
+++ b/tags/1.2.3/src/test/resources/SampleReplyText.properties
@@ -0,0 +1,17 @@
+# Copyright 2007 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# Test-specific mapping of reply code -> reply text
+# Tests are dependent on one or more values within this file
+110=Testing123
diff --git a/tags/1.2.3/src/test/resources/log4j.properties b/tags/1.2.3/src/test/resources/log4j.properties
new file mode 100644
index 0000000..b7465ac
--- /dev/null
+++ b/tags/1.2.3/src/test/resources/log4j.properties
@@ -0,0 +1,6 @@
+# Set root category priority to INFO and set its only appender to CONSOLE
+log4j.rootCategory=INFO, CONSOLE
+
+log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
+log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
+log4j.appender.CONSOLE.layout.ConversionPattern=%d{ISO8601} %c{1} [%t] %p - %m%n
diff --git a/tags/1.2.3/src/test/resources/stubftpserver-beans.xml b/tags/1.2.3/src/test/resources/stubftpserver-beans.xml
new file mode 100644
index 0000000..89918f5
--- /dev/null
+++ b/tags/1.2.3/src/test/resources/stubftpserver-beans.xml
@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 2007 the original author or authors.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!-- Spring Framework configuration for StubFtpServer -->
+<!-- The SpringConfigurationTest class has dependencies on
+ several of the bean names and values configured within this file -->
+
+<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="serverControlPort" value="9981" />
+ <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> \ No newline at end of file