1.0. Let's get started!Components wiring
FTP state is the central piece which every other component has access to. It provides the command factory dependency which in turn creates a command. FTP command
First stop in our journey will be a command implementation. Something dead simple:X-TIME command that replies with the server current time. Optional parameter specifies the date format.case class XTimeCommand(param: String, session: Session) extends Command {
override def exec: Reply = {
val sdf = new SimpleDateFormat(
if (param.isEmpty) "dd/MM/yyyy HH:mm:ss" else param)
val date = sdf.format(new Date)
Reply(200, s"Server time $date")
}
}
All commands must extend the
Command trait and take user's Session as the mandatory argument. The optional argument param is a text user enters after command's name, e.g. X-TIME ddMMyyyy where X-TIME is the command's name and ddMMyyyy is the parameter. The exec method returns a Reply.Command factory
Now that the command is ready, thecommand factory needs to create it as the response to user's X-TIME input. For that, a new implementation of the factory is needed. It is very easy, just extend the default command factory as this.class CustomCommandFactory extends DefaultCommandFactory {
def mycmd(name: String, param: String, session: Session): Option[Command] =
Option(name match {
case "X-TIME" => XTimeCommand(param, session)
case _ => null
})
override def cmd(name: String, param: String, session: Session): Command =
mycmd(name, param, session) getOrElse super.cmd(name, param, session)
}
Our new factory has the
mycmd method which creates a new command if a user sends X-TIME input to the server. Please note that this method is a copy/paste from the command factory itself, I did not write anything new. If a command cannot be created then the factory falls back for the default method.FTP state
The newcommand factory needs to be provided by the FTP state. Extend it!class CustomFtpState(...) extends FtpState(...) {
override val commandFactory = new CustomCommandFactory
}
The real implementation takes many parameters, but I did not feel like listing them and the parameters may change with the release.
The final piece
The new components are ready and wired, now what? How to tell the server to use those? The final piece of this puzzle is a launcher which will start the server with the newFTP state. Here is the source.class CustomLauncher extends Launcher {
override def createFtpState(system: ActorSystem): FtpState = {
// omettied code to read settings
new CustomFtpState(...)
}
}
object main {
def main(args: Array[String]) = new CustomLauncher().start()
}
Test the new command
Now it's time to test what we have accomplished, start the server! Typesbt run and choose the new main method. Connect to the server with telnet:telnet localhost 2021
Connected
$ X-TIME
200 Server time 02/12/2015 14:56:25
$ X-TIME yyyyMMdd
200 Server time 20151202


No comments:
Post a Comment