Sunday, November 23, 2008

Red5 Trunk Development - Revisited

So, I know I just posted about using the latest version of Red5 two days ago, but I feel like I was a bit brief on the subject and that there's much more to cover. It is my experience that a lot can go wrong in using Red5, so it's better to write a post that's more exhaustive than thin. Anyways, let's dive into it.

Comparing Red5 Trunk to 0.7:
There are a lot of differences in using the latest trunk versus using previous releases. The latest trunk (as of the time of this post's writing, revision 3332) utilizes the concept of "distributions," allowing you as a user to create as many Red5 servers as you can put your mind to. When reviewing the build.xml file, you should see a build target called "dist". This is the default target of the build file in Ant. When you issue "ant" from command line in this folder, the "dist" target will be called. (Likewise, you can call "ant dist" from command line, but it achieves the same result.)

Before going over what "dist" does, we need to do some brain washing. In 0.7 and before, when you need to start Red5, you go to Red5's home directory and call either the red5 or the red5-debug script. In the trunk, if you try doing this, you will be rewarded with tons of errors. Why? Because Red5 Trunk is meant to be started as a distribution, not as its own stand alone server. What does this mean for you? Well, firstly you need to understand this so-called concept of distributions.

Ok, now we can cover the Ant "dist" target. The "dist" target relies on one other target, the "jar" target. The JAR target does the following:
  1. Calls Ivy to start downloading the dependencies of Red5. Using Ivy is beyond the scope of this tutorial, but basically here's what happens. Red5 has declared that it depends on certain external JAR files that do not natively exist in the repository. Ivy contacts web sites and downloads the JAR's that Red5 needs. They are downloaded to a folder on your computer (usually a folder named .ivy2, its location varies based on your OS) called the Ivy "cache," then they are copied to the Red5/lib folder. You need to have internet access the first time you try and compile Red5 for this reason. Red5 won't compile if it can't get the JAR's it needs.
  2. Compiles all of the Java source files located in the Red5/src folder to the Red5/bin folder. If you can't see why this is necessary, you need to learn a whole lot more about Java.
  3. It then creates a JAR archive of Red5 with a manifest file declaring some attributes about the version and other things such as the Red5's classpath. The JAR file is output to Red5/red5.jar.
After all of this is done, the "dist" target is called, as you'd expect. The "dist" target copies all of Red5's essential files to a folder specified by the "dist.dir" property declared up at the top of the build.xml file in the base directory. As you can see, "dist" simply creates your distribution of Red5. Why is this important?

Distributions allow you to do a whole lot. Let's talk about some of the advantages of using distributions. First, let's say you're working on Red5 and you believe you found a new way to handle connections that would be a lot faster than the old way. (This is actually something that happened, by the way) You open up Eclipse to work on the Red5 source code, and create a new class: "org.red5.server.adapter.MultiThreadedApplicationAdapter". To test it, you would create a few distributions of Red5 by modifying the "dist.dir" property in the build.xml file. You could create the old version in a folder called "dist_old_way" and the new version employing MultiThreadedApplicationAdapter in a folder named "dist_new_way". To do this, you'd edit the "dist.dir" property in the Red5/build.xml file from this:
<property name="dist.dir" value="dist"/>
To this:
<property name="dist.dir" value="dist_old_way"/>
After doing so, you'd issue "ant" from command line in the Red5 home directory. This would create your distribution in the "dist_old_way" directory. Likewise, you'd create the "dist_new_way" distribution. You'd then start the 'old way' distribution by running the red5-bootstrap script in the directory you specified, ie: "dist_old_way/red5-bootstrap.sh" or "dist_old_way/red5-bootstrap.bat". It would start the server, you'd do some time tests, then create the new distribution with your modifications in the "dist_new_way" directory and run some tests against it. You'd then find out that MultiThreadedApplicationAdapter is much faster than ApplicationAdapter.

Another useful feature of using distributions is allowing for versioning of your distributions. As suggested by a member of the Red5 team, you could create distributions in directories like "dist_3324" and "dist_2345" to test features against different distributions. Ok, I believe that we've covered the concept of distributions quite thoroughly, so we'll move on to the next topic.

*Note: In Red5's default configuration, you can not run more than one instance of Red5 on one machine at a time. If you get a "BindException" when running your startup script, you probably have another Red5 running in the background. Be sure to shut it down. There are ways to allow for more than one Red5 instance on one machine, but it involves modifying ports that Red5 uses and that concept is beyond the scope of this tutorial but will hopefully be covered in a future tutorial by me or another Red5 user.


*Another Note: Do not try starting Red5 from the scripts contained in the base directory. They won't work. Don't do it! DON'T! Remember, you're using distributions now! Start Red5 from the scripts in the distribution's base folder. This is to save you weeks of frustration that I was unlucky enough to face.

red5-bootstrap vs. red5-debug, what the heck?
In the latest trunk, you may have noticed the new red5-bootstrap script. This is new since Red5 0.7 and I will explain it now. A lot of people including me have had problems using the standard red5 and red5-debug scripts to start the server in the latest trunk version. This is due to a problem with Red5's way of dealing with classpath entities. For more information on it, I suggest you post to the mailing list (red5@osflash.org). I myself have no idea when this will be fixed or if it is even being planned for being fixed. All that I know is that it's an issue with creating distributions. There is also a bug with the current Red5, again at the time of this post's writing, that creates problems when including required JAR files in a web application's /lib folder. I myself have not experienced this problem in particular, but that is because I put all of my required libraries, such as Hibernate libraries, inside of my distribution's global /lib folder. Let's just say I'm a little paranoid about errors and if I can avoid them, at all costs I will. Again, I'm not even sure if this is a bug or not, but I have seen posts in the mailing list concerning this many times.

So, all you really need to know is that the red5 and red5-debug scripts won't find JARs that you placed in your distribution's /lib folder. red5-bootstrap will find them. Here's a really handy rule, always favor red5-bootstrap. Again, it will save you hours of frustration. However, when you run the script, you may notice something. The red5-bootstrap script issues no output to command line when you open it up. It either says "Starting Red5 (org.red5.server.Bootstrap)..." or nothing at all and that's it. Let's address this.

Red5 is running invisibly?
It may appear after trying to run the red5-bootstrap script that Red5 is doing nothing or it's suspended. Well, there's no need to fear, the output is just going somewhere else, not to your console. The latest Red5 (rev 3332) addresses a problem that has been long existent in Red5. For the longest time, Red5 had problems with using common logging utilities in Java such as log4j and sl4j. This has now been fixed. I myself am not currently using a logging utility so I can't tell you how to get up and running with that, and besides, that's beyond the scope of what we're doing here.

For now, let's modify the red5-bootstrap script in your distribution's directory. Alternatively, you can edit the master script in the Red5 base directory to ensure that all future distributions you create have the changes applied to them and you won't have to change red5-bootstrap every time you create a new distribution. It's your call what to do, but open the red5-bootstrap script of your choosing, relative to your operating system. IE: For Windows, open the red5-bootstrap.bat file in Notepad, for other operating systems open the red5-bootstrap.sh file in a text editor. The last line in this script is the one of interest. It should read somewhat as so: (note, it will look different depending on whether you're editing the .bat or .sh script)
red5-bootstrap.bat file:
%RED5_HOME%\red5.bat 1>%RED5_HOME\log\stdout.log 2>%RED5_HOME%\log\stderr.log
red5-bootstrap.sh file:
exec $RED5_HOME/red5.sh 1>$RED5_HOME/log/stdout.log 2>$RED5_HOME/log/stderr.log
What you need to do now is change the last line of the script. You should change the script to simply read as so:
red5-bootstrap.bat file:
%RED5_HOME%\red5.bat
red5-bootstrap.sh file:
exec $RED5_HOME/red5.sh
After doing that, shut down Red5 if it is running, then start it with red5-bootstrap in your distribution's directory. Note: if you modified the master red5-bootstrap script in the Red5 base directory, you need to copy it to your existing distributions' folders. You should see output in your console. If you are still not seeing any output, make sure that you have the following entry in your distribution's conf/logback.xml file:
<appender name="CONSOLE"
class="ch.qos.logback.core.ConsoleAppender">
<layout class="ch.qos.logback.classic.PatternLayout">
<Pattern>[%p] [%thread] %logger - %msg%n</Pattern>
</layout>
</appender>

And that it's referenced in the "root" element in that file as so:
<root>
<level value="DEBUG" />
<appender-ref ref="CONSOLE" />
<appender-ref ref="FILE" />
<appender-ref ref="ERRORFILE" />
</root>
If you're still having problems with this, comment back to this post and/or post to the mailing list.

Changes in an application's web.xml file since 0.7:
Another thing that has changed since 0.7 is in the way that your web-app definition file ("web.xml") is constructed. There have been a few major changes in the way that this file defines applications that will throw major errors and exceptions on startup. If you are seeing some very strange errors around startup of a distribution of Red5, validate that all of your applications' web.xml files have been updated if you are migrating them to the trunk server. For more information on how these files should be constructed, post to the mailing list. I myself am not too familiar with modifying these files, excepting of course the modification of the webAppRootKey property.

Hopefully this has been very helpful in getting set up to work with the latest Red5. If you have any questions or notice an error, please comment back to this post. For some of the concepts covered here, such as the web.xml file, please send messages to the mailing list, as I'm pretty unfamiliar with the modification of that file. Long live Red5!

*Note: When I refer to something as a script, IE: "the red5-debug script", I am referring to a file with the extension of either *.bat or *.sh

Labels: , , ,

Friday, November 21, 2008

Red5 Trunk Development for Dummies like Me

Ok, so I guess I'm impaired or something as far as using the Red5 trunk server. I spent around 3 weeks trying to figure out why the red5-debug script in the base directory of Red5 wouldn't start the server, only to have the stunning realization of what the /dist directory represented, then to get stuck on not being able to see log output when using bootstrap... Let's just say that I had some problems :)
Red5_-_Development_using_the_Trunk.pdf

Anyway, I wrote this quick tutorial to get you up to speed on using the trunk. Hopefully this clears up some of the issues people seem to be having in understanding the trunk server.

And, immediately following that, you should watch this video to let you know how I felt after it was all revealed to me:

Labels: , , , , ,

MySQL + Hibernate Weird Error

For future reference, never ever define a column named "read" in a Hibernate application using MySQL.

(results in hibernate hbm2ddl error, no table generation, silent fail)

Labels: , ,

Wednesday, November 5, 2008

Pros and Cons of a Singlethreaded AVM

I've been doing a lot of thinking lately about the Actionscript Virtual Machine (AVM). I know not too many developers concern themselves with diving deep into what makes Flash Player work, but I think it's important to understand the platform on which we stand and rely.

In a lot of ways, AVM is like JVM (Java Virtual Machine), and in a lot of ways, AVM is nothing like JVM. Perhaps the biggest difference is in threads. JVM is entirely multithreaded. As a Java developer, you could basically create an infinite loop creating an infinite number of threads of execution just as fast as your computer could handle it. You can also build applications that do everything synchronously, from opening server sockets and waiting for connections, opening/reading/writing to and from files, to just about anything imaginable. You can also do things asynchronously in Java, so you have the best of both worlds. AVM is explicitly single-threaded. Though AVM may run multiple threads in the background to give you the result you see, you as a developer have one and only thread to work with in development.

This is good and bad. The pros of a singlethreaded VM are as follows:
  • Developers don't have to worry about threads, synchronization, and infinite loops. As soon as you introduce threads into a language, you as a developer have a lot more to worry about. For example, if you have two threads working on the same object at the same time, you could easily throw some variables off with one thread while the other is expecting a different result.

    IE: Thread 1 is looking at an Object (which is typed as Object) and is casting it as a Number to perform some addition function on it. Thread 2, which is executing at the same time, grabs the Object and performs a subtraction function on it. Thread 1 applies the addition function it was planning on doing, and a crazy result is received. You as a developer are weeping in your chair wondering what the heck just happened.

  • Since there's only one thread, all operations taking a long (or indefinite) time are forced to be asynchronous. Meet the AVM Event flow system. Honestly, for a single-threaded VM, AVM really is powerful with its event system. Following the Observer design pattern, the event system makes it extremely easy for any developer to trigger asynchronous events at any time.
The significant cons of a single-threaded are as follows:
  • There is little spreading of damage as far as performance is concerned. If the main thread is running slowly, the whole program is running slowly. In a single-threaded VM you can't just create a thread to handle some intensive process while the main thread does nothing.

  • There is little spreading of damage as far as errors are concerned. The single-threaded AVM handles errors similarly yet different as compared to the JVM. Because of the one and only one thread that is running, developers must be careful to NEVER EVER LET AN ERROR BUBBLE TO THE TOP. If an error makes it all the way to the top to be displayed as a message in the debug player, it is an extremely bad thing for the application. I've had many bad things happen when errors are thrown. You could have an event listener for the ENTER_FRAME event break if an error happens at just the right time in just the right place. Even worse, if the error is in the ENTER_FRAME handler, performance will go down the drain. By multithreading a VM, you're typically going to have to use a lot more try..catch...finally blocks, which is good practice. Oh, and did I mention that if an error happens at runtime, it breaks whatever is next in its execution path? If you have code similar to this:
    this.doSomething();
    this.doSomethingElse();

    ...and an error is thrown in the doSomething() method, the doSomethingElse() method may never get called, throwing off your entire application.

  • You can't do things synchronously. Honestly, some times it's just easier to call File.open() then File.write() rather than construct a file object, add an event listener, try File.open(), then when it's finally loaded, do what you want with it.

  • A single-threaded VM doesn't have multiple threads :)
Although AVM2 is great and performs well for a single-threaded VM, it is quite clear that much better performance can be achieved through a mulit-threaded VM. However, it's definitely apparent there are a few problems for developers in switching over to a multithreaded VM. Hopefully we'll be able to see a multithreaded AVM (AVM3?) for harnessing even more power from the Flash Platform soon... maybe even in the next release of Actionscript.

Labels: , , ,