Author: | Joachim Bauch |
---|---|
Contact: | jojo@struktur.de |
Date: | 2006-11-15 00:22:04 +0100 (Mi, 15 Nov 2006) |
Revision: | 1540 |
Id: | MigrationGuide.txt 1540 2006-11-14 23:22:04Z jbauch |
This document describes API differences between the Macromedia Flash Communication Server / Flash Media Server 2 and Red5. It aims at helping migrate existing applications to Red5.
If you don't have an application in Red5 yet, please read the tutorial about howto create new applications first.
When implementing serverside applications, one of the most important functionalities is to get notified about clients that connect or disconnect and to be informed about the creation of new instances of the application.
Red5 specifies these actions in the interface IScopeHandler. See the API documentation for further details.
As some methods may be called multiple times for one request (e.g. connect will be called once for every scope in the tree the client connects to), the class ApplicationAdapter defines additional methods.
This class usually is used as base class for new applications.
Here is a short overview of methods of the FCS / FMS application class and their corresponding methods of ApplicationAdapter in Red5:
FCS / FMS | Red5 |
---|---|
onAppStart | appStart roomStart |
onAppStop | appStop roomStop |
onConnect | appConnect roomConnect appJoin roomJoin |
onDisconnect | appDisconnect roomDisconnect appLeave roomLeave |
The app* methods are called for the main application, the room* methods are called for rooms (i.e. instances) of the application.
You can also also use the ApplicationAdapter to check for streams, shared objects, or subscribe them. See the API documentation for further details.
Assuming you connect to rtmp://server/app/room1/room2
At first, the connection is established, so the user "connects" to all scopes that are traversed up to room2:
After the connection is established, the client object is retrieved and if it's the first connection by this client to the scope, he "joins" the scopes:
If the same client establishes a second connection to the same scope, only the connect methods will be called. If you conect to partially the same scopes, only a few join methods might be called, e.g. rtmp://server/app/room1/room3 will trigger
The appStart method currently is only called once during startup of Red5 as it currently can't unload/load applications like FCS/FMS does. The roomStart methods are called when the first client connects to a room.
FCS / FMS provide the methods acceptConnection and rejectConnection to accept and reject new clients. To allow clients to connect, no special action is required by Red5 applications, the *Connect methods just need to return true in this case.
If a client should not be allowed to connect, the method rejectClient can be called which is implemented by the ApplicationAdapter class. Any parameter passed to rejectClient is available as the application property of the status object that is returned to the caller.
Red5 supports two different ways to access the current connection from an invoked method. The connection can be used to get the active client and the scope he is connected to. The first possibility uses the "magic" Red5 object:
import org.red5.server.api.IClient; import org.red5.server.api.IConnection; import org.red5.server.api.IScope; import org.red5.server.api.Red5; public void whoami() { IConnection conn = Red5.getConnectionLocal(); IClient client = conn.getClient(); IScope scope = conn.getScope(); // ... }
The second possiblity requires the method to be defined with an argument of type IConnection as implicit first parameter which is automatically added by Red5 when a client calls the method:
import org.red5.server.api.IClient; import org.red5.server.api.IConnection; import org.red5.server.api.IScope; public void whoami(IConnection conn) { IClient client = conn.getClient(); IScope scope = conn.getScope(); // ... }
For many applications, existing classes containing application logic that is not related to Red5 are required to be reused. In order to make them available for clients connecting through RTMP, these classes need to be registered as handlers in Red5.
The handlers can be executed by clients with code similar to this:
nc = new NetConnection(); nc.connect("rtmp://localhost/myapp"); nc.call("handler.method", nc, "Hello world!");
If a handler is requested, Red5 always looks it up in the custom scope handlers before checking the handlers that have been set up in the context through the configuration file.
This method is best suited for handlers that are common to all scopes the application runs in and that don't need to change during the lifetime of an application.
To register the class com.fancycode.red5.HandlerSample as handler sample, the following bean needs to be added to WEB-INF/red5-web.xml:
<bean id="sample.service" class="com.fancycode.red5.HandlerSample" singleton="true" />
Note that the id of the bean is constructed as the name of the handler (here sample) and the keyword service.
All applications that use handlers which are different for the various scopes or want to change handlers, need a way to register them from the serverside code. These handlers always override the handlers configured in red5-web.xml. The methods required for registration are described in the interface IServiceHandlerProvider which is implemented by ApplicationAdapter.
The same class as above can be registered using this code:
public boolean appStart(IScope app) { if (!super.appStart(scope)) return false; Object handler = new com.fancycode.red5.HandlerSample(); app.registerServiceHandler("sample", handler); return true; }
Note that in this example, only the application scope has the sample handler but not the subscopes! If the handler should be available in the rooms as well, it must be registered in roomStart for the room scopes.
To call methods from your Red5 application on the client, you will first need a reference to the current connection object:
import org.red5.server.api.IConnection; import org.red5.server.api.Red5; import org.red5.server.api.service.IServiceCapableConnection; ... IConnection conn = Red5.getConnectionLocal();
If the connection implements the IServiceCapableConnection interface, it supports calling methods on the other end:
if (conn instanceof IServiceCapableConnection) { IServiceCapableConnection sc = (IServiceCapableConnection) conn; sc.invoke("the_method", new Object[]{"One", 1}); }
If you need the result of the method call, you must provide a class that implements the IPendingServiceCallback interface:
import org.red5.server.api.service.IPendingService; import org.red5.server.api.service.IPendingServiceCallback; class MyCallback implements IPendingServiceCallback { public void resultReceived(IPendingServiceCall call) { // Do something with "call.getResult()" } }
The method call looks now like this:
if (conn instanceof IServiceCapableConnection) { IServiceCapableConnection sc = (IServiceCapableConnection) conn; sc.invoke("the_method", new Object[]{"One", 1}, new MyCallback()); }
Of course you can implement this interface in your application and pass a reference to the application instance.
The methods to access shared objects from an application are specified in the interface ISharedObjectService.
When dealing with shared objects in serverside scripts, special care must be taken about the scope they are created in.
To create a new shared object when a room is created, you can override the method roomStart in your application:
import org.red5.server.adapter.ApplicationAdapter; import org.red5.server.api.IScope; import org.red5.server.api.so.ISharedObject; public class SampleApplication extends ApplicationAdapter { public boolean roomStart(IScope room) { if (!super.roomStart(room)) return false; createSharedObject(room, "sampleSO", true); ISharedObject so = getSharedObject(room, "sampleSO"); // Now you could do something with the shared object... return true; } }
Now everytime a first user connects to a room of a application, e.g. through rtmp://server/application/room1, a shared object sampleSO is created by the server.
If a shared object should be created for connections to the main application, e.g. rtmp://server/application, the same must be done in the method appStart.
For further informations about the possible methods a shared object provides please refer to the api documentation of the interface ISharedObject.
To get notified about changes of the shared object similar to onSync in FCS / FMS, a listener must implement the interface ISharedObjectListener:
import org.red5.server.api.so.ISharedObject; import org.red5.server.api.so.ISharedObjectListener; public class SampleSharedObjectListener implements ISharedObjectListener { public void onSharedObjectUpdate(ISharedObject so, String key, Object value) { // The attribute <key> of the shared object <so> // was changed to <value>. } public void onSharedObjectDelete(ISharedObject so, String key) { // The attribute <key> of the shared object <so> was deleted. } public void onSharedObjectSend(ISharedObject so, String method, List params) { // The handler <method> of the shared object <so> was called // with the parameters <params>. } // Other methods as described in the interface... }
Additionally, the listener must get registered at the shared object:
ISharedObject so = getSharedObject(scope, "sampleSO"); so.addSharedObjectListener(new SampleSharedObjectListener())
A shared object can be changed by the server as well:
ISharedObject so = getSharedObject(scope, "sampleSO"); so.setAttribute("fullname", "Sample user");
Here all subscribed clients as well as the registered handlers are notified about the new / changed attribute.
If multiple actions on a shared object should be combined in one update event to the subscribed clients, the methods beginUpdate and endUpdate must be used:
ISharedObject so = getSharedObject(scope, "sampleSO"); so.beginUpdate(); so.setAttribute("One", "1"); so.setAttribute("Two", "2"); so.removeAttribute("Three"); so.endUpdate();
The serverside listeners will receive their update notifications through separate method calls as without the beginUpdate and endUpdate.
Calls to shared object handlers through remote_so.send(<handler>, <args>) from a Flash client or the corresponding serverside call can be mapped to methods in Red5. Therefore a handler must get registered through a method of the ISharedObjectHandlerProvider interface similar to the application handlers:
package com.fancycode.red5; class MySharedObjectHandler { public void myMethod(String arg1) { // Now do something } } ... ISharedObject so = getSharedObject(scope, "sampleSO"); so.registerServiceHandler(new MySharedObjectHandler());
Handlers with a given name can be registered as well:
ISharedObject so = getSharedObject(scope, "sampleSO"); so.registerServiceHandler("one.two", new MySharedObjectHandler());
Here, the method could be called through one.two.myMethod.
Another way to define event handlers for SharedObjects is to add them to the red5-web.xml similar to the file-based application handlers. The beans must have a name of <SharedObjectName>.<DottedServiceName>.soservice, so the above example could also be defined with:
<bean id="sampleSO.one.two.soservice" class="com.fancycode.red5.MySharedObjectHandler" singleton="true" />
Persistence is used so properties of objects can be used even after the server has been restarted. In FCS / FMS usually local shared objects on the serverside are used for this.
Red5 allows arbitrary objects to be persistent, all they need to do is implement the interface IPersistable. Basically these objects have a type, a path, a name (all strings) and know how to serialize and deserialize themselves.
Here is a sample of serialization and deserialization:
import java.io.IOException; import org.red5.io.object.Input; import org.red5.io.object.Output; import org.red5.server.api.persistence.IPersistable; class MyPersistentObject implements IPersistable { // Attribute that will be made persistent private String data = "My persistent value"; void serialize(Output output) throws IOException { // Save the objects's data. output.writeString(data); } void deserialize(Input input) throws IOException { // Load the object's data. data = input.readString(); } // Other methods as described in the interface... }
To save or load this object, the following code can be used:
import org.red5.server.adapter.ApplicationAdapter; import org.red5.server.api.IScope; import org.red5.server.api.Red5; import org.red5.server.api.persistence.IPersistenceStore; class MyApplication extends ApplicationAdapter { private void saveObject(MyPersistentObject object) { // Get current scope. IScope scope = Red5.getConnectionLocal().getScope(); // Save object in current scope. scope.getStore().save(object); } private void loadObject(MyPersistentObject object) { // Get current scope. IScope scope = Red5.getConnectionLocal().getScope(); // Load object from current scope. scope.getStore().load(object); } }
If no custom objects are required for an application, but data must be stored for future reuse, it can be added to the IScope through the interface IAttributeStore. In scopes, all attributes that don't start with IPersistable.TRANSIENT_PREFIX are persistent.
The backend that is used to store objects is configurable. By default persistence in memory and in the filesystem is available.
When using filesystem persistence for every object a file is created in "webapps/<app>/persistence/<type>/<path>/<name>.red5", e.g. for a shared object "theSO" in the connection to "rtmp://server/myApp/room1" a file at "webapps/myApp/persistence/SharedObject/room1/theSO.red5" would be created.
Applications that need to perform tasks regularly can use the setInterval in FCS / FMS to schedule methods for periodic execution.
Red5 provides a scheduling service (ISchedulingService) that is implemented by ApplicationAdapter like most other services. The service can register an object (which needs to implement the IScheduledJob interface) whose execute method is called in a given interval.
To register an object, code like this can be used:
import org.red5.server.api.IScope; import org.red5.server.api.IScheduledJob; import org.red5.server.api.ISchedulingService; import org.red5.server.adapter.ApplicationAdapter; class MyJob implements IScheduledJob { public void execute(ISchedulingService service) { // Do something } } public class SampleApplication extends ApplicationAdapter { public boolean roomStart(IScope room) { if (!super.roomStart(room)) return false; // Schedule invokation of job every 10 seconds. String id = addScheduledJob(10000, new MyJob()); room.setAttribute("MyJobId", id); return true; } }
The id that is returned by addScheduledJob can be used later to stop execution of the registered job:
public void roomStop(IScope room) { String id = (String) room.getAttribute("MyJobId"); removeScheduledJob(id); super.roomStop(room); }
Remoting can be used by non-rtmp clients to invoke methods in Red5. Another possibility is to call methods from Red5 to other servers that provide a remoting service.
Services that should be available for clients need to be registered the same way as additional application handlers are registered. See above for details.
To enable remoting support for an application, the following section must be added to the WEB-INF/web.xml file:
<servlet> <servlet-name>gateway</servlet-name> <servlet-class> org.red5.server.net.servlet.AMFGatewayServlet </servlet-class> </servlet> <servlet-mapping> <servlet-name>gateway</servlet-name> <url-pattern>/gateway/*</url-pattern> </servlet-mapping>
The path specified in the <url-pattern> tag (here gateway) can be used by the remoting client as connection url. If this example would have been specified for an application myApp, the URL would be:
http://localhost:5080/myApp/gateway
Methods invoked through this connection will be executed in the context of the application scope. If the methods should be executed in subscopes, the path to the subscopes must be added to the URL like:
http://localhost:5080/myApp/gateway/room1/room2
The class RemotingClient defines all methods that are required to call methods through the remoting protocol.
The following code serves as example about how to use the remoting client:
import org.red5.server.net.remoting.RemotingClient; String url = "http://server/path/to/service"; RemotingClient client = new RemotingClient(url); Object[] args = new Object[]{"Hello world!"}; Object result = client.invokeMethod("service.remotingMethod", args); // Now do something with the result
By default, a timeout of 30 seconds will be used per call, this can be changed by passing a second parameter to the constructor defining the maximum timeout in milliseconds.
The remoting headers AppendToGatewayUrl, ReplaceGatewayUrl and RequestPersistentHeader are handled automatically by the Red5 remoting client.
Some methods may take a rather long time on the called server to complete, so it's better to perform the call asynchronously to avoid blocking a thread in Red5. Therefore an object that implements the interface IRemotingCallback must be passed as additional parameter:
import org.red5.server.net.remoting.RemotingClient; import org.red5.server.net.remoting.IRemotingCallback; public class CallbackHandler implements IRemotingCallback { void errorReceived(RemotingClient client, String method, Object[] params, Throwable error) { // An error occurred while performing the remoting call. } void resultReceived(RemotingClient client, String method, Object[] params, Object result) { // The result was received from the server. } } String url = "http://server/path/to/service"; RemotingClient client = new RemotingClient(url); Object[] args = new Object[]{"Hello world!"}; IRemotingCallback callback = new CallbackHandler(); client.invokeMethod("service.remotingMethod", args, callback);
《JavaE序员的推荐阅读书籍?/p>
JavaEye (http://www.javaeye.com)
范凯(http://robbin.javaeye.com)
作ؓJavaE序员来_最痛苦的事情莫q于可以选择的范围太q,可以ȝ书太多,往往Ҏ无所适从。我惛_我自pq的技术书c中挑选出来一些,按照学习的先后顺序,推荐l大Ӟ特别是那些想不断提高自己技术水q的JavaE序员们?/p>
一、Java~程入门c?/span>
对于没有Java~程l验的程序员要入门,随便M么入门书c都一Pq个阶段需要你快速的掌握Java基础语法和基本用法,宗旨是“囫囵吞枣不求甚解”Q先对Java熟悉h再说。用很短的时间快速过一遍Java语法Q连懵带猜多写写代码Q要“知其?#8221;?/p>
1、《Java~程思想?
在有了一定的Java~程l验之后Q你需?#8220;知其所以然”了。这个时候《Java~程思想》是一本让你知其所以然的好书,它对于基本的面向对象知识有比较清楚的交待Q对Java基本语法Q基本类库有比较清楚的讲解,可以帮你打一个良好的Java~程基础。这本书的缺Ҏ实在太厚Q也比较|嗦Q不适合C人快节奏学习Q因此看q本书要懂得取舍Q不是每章每节都值得一看的Q挑重点的深入看可以了?/p>
2、《Agile Java》中文版
q本书是出版CN给我的Q我一拿到束之高阁,攑֜书柜一都没有过Q但是前两天整理书柜的时候,拿出来一,竟然发现q绝Ҏ一本好书!q本书一大特Ҏ以单元测试和TDD来诏I全书的Q在教你Java各种重要的基知识的过E中Q潜U默化的影响你的~程思维走向敏捷Q走向TDD。另外这本书成书很新Q以JDK5.0的语法ؓ基础讲解Q要学习JDK5.0的新语法也不错。还有这本书对于内容取舍也非常得当,Java语言毕竟cd庞大Q可以讲的内容太多,q本书选择的内容以及内容的多寡都很得当Q可以让你以最的旉掌握Java最重要的知识,Z培养出来优秀的编E思\Q真是一本不可多得的好书?/p>
虽然作者自己把q本书定位在入门U别Q但我不定q本书用来入门是不是E微׃点,我自׃准备有空的时候翻这本书Q学习学习?/p>
二、Java~程q阶c?/span>
打下一个良好的Java基础Q还需要更多的实践l验U篏Q我x有什么捷径。有两本书值得你在~程生的这个阶D阅读,培养良好的编E习惯,提高你的代码质量?/p>
1、《重?改善既有代码的设计?
q本书名气很大,不用多介l,可以在闲暇的时候多ȝQ多和自q实践怺印证。这本书对你产生影响是潜U默化的?/p>
2、《测试驱动开?by Example?
本书最大特Ҏ很薄Q看h没有什么负担。你可以找一个周末的下午Q一边看Q一边照做,一个下午就把书看完Q这本书的所有例子跑完了。这本书的作用是通过实战让你培养TDD的思\?/p>
三、Java架构师之?/span>
到这个阶D,你应该已l非常娴熟的q用Java~程Q而且有了一个良好的~程思\和习惯了Q但是你可能q缺乏对应用软g整体架构的把握,现在是你迈向架构师的第一步?/p>
1、《Expert One-on-One J2EE Design and Development?
q本书是Rod Johnson的成名著作,非常l典Q从q本书中的代码诞生了springframework。但是好像这本书没有中译本?/p>
2、《Expert One-on-One J2EE Development without EJB?
q本书由gigixl织译Q多位业界专家参与,虽然|名译者是JavaEyeQ其实JavaEye出力不多Q实在是忝居译者之名?/p>
以上两本书都是Rod Johnson的经典名著,Java架构师的必读书籍。在我所推荐的这些书c当中,是我看过的最仔细Q最认真的书Q我当时读这本书几乎是废寝忘食的一气读完的Q有时候挑灯夜读金庸武侠小说的劲头Q书中所讲内容和自己的经验知识一一印证Q又被无比精辟的ȝ出来Q读完这本书以后Q我有种被打通经脉,功力爆增的感觉?/p>
但是后来我看q一些其他h的评Pg阅读体验q没有我那么highQ也许是因ؓ每个人的知识U篏和经验不同导致的。我那个时候刚好是l验知识U篏已经_丰富Q但是还没有pȝ的整理成型,让这本书一梳理Q立dŞ成完整的知识体系了?/p>
3、《企业应用架构模式?
Martin的又一本名著,但这本书我只是泛泛的看了一遍,q没有仔l看。这本书g更适合做框架的人去看,例如如果你打自己写一个ORM的话Q这本书是一定要看的。但是做应用的hQ不看貌g无所谓,但是如果有空Q我q是推荐认真看看Q会让你知道框架Z么要q样设计Q这样你的层ơ可以晋升到框架设计者的角度L考问题。Martin的书我向来都是推崇,但是从来都没有像Rod Johnson的书那样非常认真ȝ?/p>
4、《敏捯Y件开?原则、模式与实践?
Uncle Bob的名著,敏捷的经典名著,q本书比较特别,与其说是讲Y件开发过E的书,不如说讲软g架构的书Q本书用了很大篇q讲各种面向对象软g开发的各种模式Q个Z为看了这本书Q就不必看GoF的《设计模式》了?/p>
四、Y件开发过E?/span>
了解软g开发过E不单纯是提高程序员个h的良好编E习惯,也是增强团队协作的基?/p>
1、《UML_a?
UML其实和Y件开发过E没有什么必然联p,却是软g团队协作沟通,撰写软g文档需要的工具。但是UML真正实用的图不多Q看看这本书已经_了,完全没有必要d《UML用户指南》之cȝ东西。要提醒大家的是Q这本书的中译本译的非怹烂,有条件的看英文原版?/p>
2、《解析极限编E?拥抱变化》XP
q是Kent Beck名著的第二版Q中英文对照。没什么好说的Q必Mc?/p>
3、《统一软g开发过E》UP
其实UP和敏捷ƈ不一定冲H,UP也非常强调P代,试Q但是UP的文档和q程驱动却是敏捷所不取的。不怎么_UP值得你去读,毕竟在中国真正接受敏L企业很少Q你q是需要用UP来武装一下自qQ哪怕是披着UP的XP?/p>
4、《敏捷徏模》AM
Scott Ambler的名著,q本书非常的progmaticQ告诉你怎么既敏捷又UPQ把敏捷和UPl一h了,又提Z很多progmatic的徏议和做法。你可以把《解析极限编E?拥抱变化》、《统一软g开发过E》和《敏捷徏模》这三本书放在一赯Q看XP和UP的不同点Q再看AM是怎么l一XP和UP的,把这三种理论融ؓ一炉,形成自己的理Zp,那么你也可以d书了?/p>
五、Y仉目管?/span>
如果你突然被领导提拔为项目经理,而你完全没有目理l验Q你肯定会心里没底;如果你觉得自q理项目不善,很想改善你的目理能力Q那么去考PMP肯定是远水不解近渴的?/p>
1、《快速Y件开发?
q也是一本名著。可以这栯Q有本书在手Q你有了一个项目管理的高参谋l你划策Q再也不必担心自׃能胜ȝ问题了。这本书不是讲管理的理论的,在实际的目理中,讲这些理论是不解决问题的Q这本书有点cM?#8220;软g目点子大全”之类的东西,列D了种UY仉目当中面临的各种问题Q以及应该如何解决问题的点子Q你只需要稍加变通,找方抓药p了?/p>
六、ȝ
在这份推荐阅Mc的名单中,我没有列举流行的软g框架cd习书c,例如StrutsQHibernateQSpring之类Q也没有列DAJAX斚w的书c。是因ؓq类书籍Ҏq时Q而上q的大半书籍的生命周期都_长,值得你去购买和收藏?/p>