Monday, 14 December 2015

My very own FTP command

Greetings everyone, today I will show you how to add your custom command to the Akka-FTP server. You will need a decent IDE, preferably IntelliJ, and the knowledge of Scala. The example for this chapter available here and compile against Akka-FTP tag 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, the command 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 new command 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 new FTP 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! Type sbt 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

Summary 

In this chapter I explained how to extend Akka-FTP components and start the server with new dependencies. This is the basics of extending the Akka-FTP and you should follow those steps to plug-in your own add-ons to the server. Enough for today, thanks for reading!

No comments:

Post a Comment