Red5 Application Deployment with Capistrano

June 25th, 2007

Introduction

To deploy the applications for Red5, I found that Rubys Capistrano really fits very nice. This is an abstract on how we did it.

Basic Requirements

Subversion repository with your Red5 Application checked in (the compiled .class files) with the following structure (or adjust the script below):

  • webapps/config
  • webapps/MyRed5Application
  • webapps/red5-default.xml

On the server

  • Installed and configured Red5 server
  • Secure socket shell access to the server
  • Subversion client (svn)

On the client from which to deploy

  • Ruby >1.8.4
  • Gems
  • Capistrano (gem install capistrano)

deploy.rb

The following Capistrano recipe will provide you with the functionality of automatic deployment and rollback (it uses Capistranos out-of-the-box features where possible). Put this file (”deploy.rb”) in a directory called “config”. Be sure to adjust all parameters starting with a “#”.

set :application, “webapps”
set :repository, “#svn-path-to-your-webapps” # i.e. https://server.com/repos/red5apps/webapps
set :svn_username, “#svn_user” # svn user to use for checking out the application
set :svn_password, Proc.new { Capistrano::CLI.password_prompt(’SVN Password: ‘) }
# app roles
role :app, “#example.com” # domain with s.s.h. access to deploy to
set :user, “#user” # user used for login
# directories
red5_dir = “#/etc/red5″ # path on the server to your red5 directory
set :deploy_to, “#/etc/red5/deploy_to_directory” # directory where to check out your svn code
# tasks
task :restart do
run “cd #{red5_dir} ; #{red5_dir}/red5-shutdown.sh >> #{red5_dir}/red5.log”
run “cd #{red5_dir} ; nohup #{red5_dir}/red5.sh >> #{red5_dir}/red5.log &\nsleep 5″
end
# overridden task
desc <<-DESC
Update all servers with the latest release of the source code. All this does
is do a checkout (as defined by the selected scm module).
DESC
task :update_code, :except => { :no_release => true } do
on_rollback { delete release_path, :recursive => true }
source.checkout(self)
set_permissions
# uncache the list of releases, so that the next time it is called it will
# include the newly released path.
@releases = nil
end

Now what

First step is to create the initial directory layout for your application on the server. Run the following command in the terminal/shell in the “webapps” directory:

cap setup

When started, Capistrano will ask for your password to log into the server and create the appropriate directories you specified in the deploy.rb recipe. You can read more about this in the Capistrano manual, but basically it creates subdirectories like “releases”, “shared” where the application is going to be placed. Each deploy will create a new release and the directory “current” will be a symlink to the current release.

Next step on the server is to replace the Red5 “webapps” directory and create a symbolic link:

cd #/etc/red5
rm -rf webapps
ln -s #/etc/red5/deploy_to_directory/current webapps

This makes sure that Red5 will always take the latest deployed release.

The final step is to deploy the application:

cap deploy

This will checkout the latest release to the server, change the “current” symlink to this release (Red5 was configured to use the “current” directory) and will restart the Red5 server to use the latest version. If you’re running Tomcat it should be easy to change the task :restart…

And finally, in case you want to roll back to your previous version, just execute

cap rollback

on your client and Capistrano will take care of it.

Share This

Convert Generic Objects into Class Instances using JSON Schema

June 9th, 2007

To convert plain old Actionscript objects into Value Objects I’ve created a generator (still in internal pre alpha mode) that will take (what I call) a “JSON Schema definition” file and generate the Actionscript Value Object classes.
In the “JSON Schema definition” (like XML Schema), the data structure is declared (but unlike XSDs, this is lightweight and human readable). In fact, its valid JSON itself (so the parsing code in the generator reduces itself to a simple json.parse(…)).

You can also define nested structures (typed objects containing other typed objects) as well as typed arrays (array containing typed objects).

To proceed with an example, feed the generator the following JSON definition file:

{
  “_class”: “Book”,
  “title”: “String”,
  “author”: “String”,
  “chapters”:
  [
    {
      “_class”: “Chapter”,
      “name”: “String”,
      “pages”: “Int”
    }
  ]
}

And it will generate the following classes (package declarations omitted for readability):

public class Book
{
  public var title:String;
  public var author:String;
  public var chapters:ChapterArray;

  public function Book(o:Object)
  {
    title = o.title;
    author = o.author;
    chapters = new ChapterArray(o.chapters);
  }
}

public class Chapter
{
  public var name:String;
  public var pages:int;

  public function Chapter(o:Object)
  {
    name = o.name;
    pages = o.pages;
  }
}

public dynamic class ChapterArray extends Array
{
  public function ChapterArray(o:Object)
  {
    super();
    for each(var item:Object in o)
    {
      var chapter:Chapter = new Chapter(item);
      this.push(chapter);
    }
  }
}

Now all you have to do when receiving plain old objects (via a webservice, json/http or any other method), all you have to do to convert them to typed objects is to call

var book:Book = new Book(event.result);

And you’ll have all the benefits of having typed objects (like complete IDE support, etc.) as well as having your interface documented!

I’m interested in feedback about this method, so feel invited to comment.

Share This

Managing Flex Modules

May 13th, 2007

When working with modules the “edit-compile-run” cycle can get in your way: Compiling the main application and then the separate modules, managing multiple Flex Builder/Ant projects, etc. For developing, it would be best to have just one big project. Here is how we do it…

  • Keep each of your modules in a separate directory, declaring it as mx:Module (in this example the module is ImageManager)
  • In your main application beneath your src directory, have the following two source directories:
    Directory Layout

    1. mod-access/extern
      The ImageManagerAccess.mxl here is just a wrapper for the mx:ModuleLoader:

      <mx:ModuleLoader xmlns:mx=“http://www.adobe.com/2006/mxml” width=“100%” height=“100%” creationComplete=“url=’ImageManager.swf’; loadModule()”/>
    2. mod-access/embed
      The ImageManagerAccess.xml includes the Module ImageManager itself:

      <local:ModAccessEmbed xmlns:imageManager=“imageManager.*” xmlns:local=“*” width=“100%” height=“100%”>
            <imageManager:imageManager/>
          </local:ModAccessEmbed>

      The class ModAccessEmbed mimics the behavior of the mx:ModuleLoader class. The code shows an excerpt

      public class ModAccessEmbed extends Canvas
          {
            public function loadModule() : void
            {       
              …
            }
            public var url:String;
            … // additional methods from mx:ModuleLoader as needed 
          }
  • Wherever you want the module to appear, don’t use a mx:ModuleLoader, but the corresponding ImageManagerAccess
  • If you want your Flex project to contain all modules code in the main .swf, put mod-access/embed into your projects source path. If you want to build it separately, put mod-access/extern into your source path and remove the mod-access/embed line.

This way, you can easily switch (by just swapping the source pathes) between building one swf that contains the complete modules and compiling main application and modules separately. Or use Flex Builder to develop the integrated version and an Ant build.xml for compiling the final application.swf as well as the swf for the modules.

Useful links for working with modules/RSLs:

Share This

The Humble MXML

May 12th, 2007

While thinking about how to separate logic and user interface code on my current Flex project, I rerun into Michael Feathers great article about the “Humble Dialog Box”. I used this approach a couple of years before on a C++ project, was happy with it and I am now implementing it again. This article is about how to realize this separation in a Flex/MXML environment.

The basic idea of separating logic and ui code is propably as old as the first graphical user interfaces itself. Even older is the tendency to “just add the cool new feature” then “applying a fix here” and before your realize it, you’re stuck with the ui class handling everything. To cite from the article: “It is easy to just override an event from a component and drop your interaction logic right there in the dialog box class.”

And the Flex/MXML ecosystem is no exception: As easy as Flex makes it to chum out MXML code that looks and work like a charm, it is also really easy to be led into a design which puts everything into the MXML file itself. The examples by Adobe are leading you exactly there. To be fair, their goal is coming to the point of showing off what the Flex Framework can do and not educating you about proper practices.

Example

I’ll describe the concepts with my component ImageManager as an example. First a screenshot of the component:

ImageManager component

ImageManager will provide the user a list of tags (the list on the left hand side) and (depending on which tag was chosen by the user) display a TileList of Images according to the tag. Search tags can also be entered manually by the user via the TextInput field. The real search is done on a Ruby on Rails server in the background (but that’s another topic). If a user clicks on a tag, the TextInput field will be updated with the search tags (could be words more than just the tag from the list).

Concept and Realisation

The Humble Dialog Box (in this case The Humble MXML) suggests to refactor the .MXML file into 3 entities which each have exactly one reason to exist (and only one reason to change):

  1. ImageManager.mxml: The user interface itself, the MXML class. This file should contain mostly MXML Nodes and almost no code
  2. ImageManagerLogic.as: The class handling the logic and only the logic. It communicates with the interface via the 3rd element, the interface
  3. IImageManager.as: The protocol to transfer information from the logic to the user interface

Taking the logic out of the MXML and putting it into a separate class is pretty obvious. The ImageManager.mxml uses the ImageManagerLogic directly via a member-instance.

The interesting point in “The Humble MXML” is how to do the communication from the logic to the mxml. We don’t want the logic to depend on the user interface, so we obviously can’t reference the MXML file from the ImageManagerLogic. This is where the interface comes in: Put all callbacks from the logic into the IImageManager interface. In this example, when a user clicks the tags the TextInput has to be filled with the tags calculated by ImageManagerLogic. The IImageManager then looks like this:

interface IImageManager {   function set searchTags(t:String):void; }

The ImageManagerLogic uses the IImageManager by calling _view.searchTags = …:

class ImageManagerLogic {   private var _view : IImageManager;   public function set view(v:IImageManager):void   {     _view = v;   }   public function selectTag(index:int):void   {     _view.searchTags = tags[index].label;     // then go on and trigger the search   }   // various private methods hidden   public function searchForTags(tags:String):void   {     // do the search   } }

In the implementation of ImageManager.mxml the following happens:

  • The ImageManager.mxml implements the IImageManager interface (implements=”IImageManager”) and tells the logic to use it (logic.view = this)
  • The implementation of the IImageInterface just puts the tags into the TextInput field
  • All event handlers in the Layout just call the logic directly
<mx:module implements=“IImageManager” initialize=“logic.view = this”>
<mx:script>
<!–[CDATA[
ImageManagerLogic logic;

// implements IImageManager
public function set searchTags(tags:String):void
{
inputSearchTags.text = tags;
}
]]–>
</mx:script>
<mx:hdividedbox width=“100%” height=“100%”>
<mx:list width=“200″ height=“100%” id=“list”>
dataProvider="{logic.tags}"
click="logic.selectTag(list.selectedIndex)" /&gt;
<mx:vbox width=“100%” height=“100%”>
<mx:hbox>
<mx:textinput id=“inputSearchTags”>
enter="logic.searchForTags(inputSearchTags.text)"/&gt;
<mx:button label=“search”>
click="logic.searchForTags(inputSearchTags.text)"/&gt;
</mx:button>
<mx:tilelist dataprovider=“{logic.images}”>
</mx:tilelist>
</mx:textinput>
</mx:hbox>
</mx:vbox></mx:list></mx:hdividedbox></mx:module>

So, what do you get?

  • Clear design: It is pretty obvious where to put stuff and where to look for it. If it is how something should look like (layout, formatting), it belongs into the .mxml file. If it processes data or calculates stuff, it belongs to the logic
  • Reusability: It is easy to e.g. create an advanced ui/dialog by just wiring a new MXML class with the existing logic code. Or use the logic somewhere else, perhaps reusing it in another, more complex logic block
  • Testability of the logic (by using a mock object for the view): Because the MXML just contains call to the logic if something happens, it is very probably the bug is in the logic - there you can have automated tests and need not to worry about the gui

Some things to consider

  • The only code allowed in the markup part of the .MXML file are calls to the logic layer. If you need complex calculations of input data (e.g. combining strings) for the logic layer this could be a sign that this functionality belongs into the logic. If it really doesn’t, put it in the Script part
  • If there are no callbacks from the logic layer to the gui, you don’t need the interface IImageManager

What does it cost?

  • You end up with 3 files instead of having just one. For small projects this may look like overkill, but even small projects can grow up real fast
  • Having to abstract the communication between the logic and the ui via an interface. For more complex patterns, this can be a real pain, but it is better to think about the protocol before instead of trying to seperate logic and ui at a later time when things got messed up

Opinions?

  • Do you consider this overkill?
  • Which unit testing frameworks do you use? Experiences?
Share This

Close
E-mail It
Socialized through Gregarious 42