2007年10月12日

site: http://www.joachim-bauch.de/tutorials/red5/MigrationGuide.txt

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

Preface

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.

Application callbacks

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.

Interface IScopeHandler

Red5 specifies these actions in the interface IScopeHandler. See the API documentation for further details.

Class ApplicationAdapter

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.

Execution order of connection methods

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:

  1. app (-> appConnect)
  2. room1 (-> roomConnect)
  3. room2 (-> roomConnect)

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:

  1. app (-> appJoin)
  2. room1 (-> roomJoin)
  3. room2 (-> roomJoin)

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

  1. appConnect("app")
  2. joinConnect("room1")
  3. joinConnect("room3")
  4. roomJoin("room3")

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.

Accepting / rejecting clients

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.

Current connection and client

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();
// ...
}

Additional handlers

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.

There are currently two ways to register these handlers:
  1. By adding them to the configuration files.
  2. By registering them manually from the application code.

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.

Handlers in configuration files

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.

Handlers from application code

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.

Calls to client methods

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.

SharedObjects

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.

Serverside change listeners

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())
Changing from application code

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.

SharedObject event handlers

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

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.

Periodic events

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

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.

Remoting server

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
Remoting client

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);

posted @ 2007-10-12 14:09 zibeline 阅读(523) | 评论 (0)编辑 收藏

2007年10月9日


site: http://www.flashseer.org/bbs/viewthread.php?tid=614&extra=page%3D1

要用到openlaszlo,配置了一下开发环境。
把过程记录下来。如果其他人也需要用,希望有帮助。

http://www.openlaszlo.org/download 下载安装文件

安装完成后可以看到openlaszlo自带了一个tomcat,你访问的就是这个自带的tomcat的一个应用。
openlaszlo和flex类似。也是根据xml语言,编译生成文件,不过,openlaslzo可以选择生成swf,dhtml两种格式。
openlaszlo的开发工具比较原始,ide4laszlo已经被废弃掉了。文档上的两种开发方式,
第一是在laszlo in 10 minutes 里面,有个界面,写代码,然后编译,这个方式在学习的时候用
第二在开发的时候用,用文本编辑器编辑,然后输入网址访问这个lzx文件。根据下面的控制面板编译。

我机器上本来就有tomcat,还有其它应用。要用到的仅仅是安装文件里面的那个应用。

用myeclipse建立一个j2ee工程,名称为laszlo。

把安装目录下:
x:\Program Files\OpenLaszlo Server 4.0.5\Server\lps-4.0.5

lps                            laszlo的组件库
WEB-INF\lps               laszlo的配置文件
WEB-INF\lib                jar包
WEB-INF\web.xml       web应用的配置文件

分别拷贝到webapp对应的目录下 。

然后在建立一个client目录,新建一个hello.lzx。
输入
<canvas>
    <text>Hello Laszlo!</text>
</canvas>

你可以发布,或者在tomcat中指向现在这个工程。

重启你的tomcat,访问 http://localhost:8080/laszlo/hello.lzx 就可以访问了。

编辑lzx文件,我装了一个xmlbuddy的eclipse插件,把它当xml来编辑。
但是xmlbuddy和myeclipse的xml editor有冲突,xml editor又不能指定去编辑lzx,有点郁闷,但是可以忍。不用了,再把xmlbuddy卸了就行了。

还有想查阅laszlo的文档。就把x:\Program Files\OpenLaszlo Server 4.0.5\Server\lps-4.0.5拷贝到你tomcat的webapps下,当成一个应用,直接用
http://127.0.0.1:8080/lps-4.0.5
就可以访问了。

posted @ 2007-10-09 14:29 zibeline 阅读(601) | 评论 (1)编辑 收藏

2007年10月8日

site: http://www.javaeye.com/topic/52992

《Java程序员的推荐阅读书籍》

JavaEye (http://www.javaeye.com)

范凯(http://robbin.javaeye.com)

作为Java程序员来说,最痛苦的事情莫过于可以选择的范围太广,可以读的书太多,往往容易无所适从。我想就我自己读过的技术书籍中挑选出来一些,按照学习的先后顺序,推荐给大家,特别是那些想不断提高自己技术水平的Java程序员们。

一、Java编程入门类

对于没有Java编程经验的程序员要入门,随便读什么入门书籍都一样,这个阶段需要你快速的掌握Java基础语法和基本用法,宗旨就是“囫囵吞枣不求甚解”,先对Java熟悉起来再说。用很短的时间快速过一遍Java语法,连懵带猜多写写代码,要“知其然”。

1、《Java编程思想》

在有了一定的Java编程经验之后,你需要“知其所以然”了。这个时候《Java编程思想》是一本让你知其所以然的好书,它对于基本的面向对象知识有比较清楚的交待,对Java基本语法,基本类库有比较清楚的讲解,可以帮你打一个良好的Java编程基础。这本书的缺点是实在太厚,也比较罗嗦,不适合现代人快节奏学习,因此看这本书要懂得取舍,不是每章每节都值得一看的,挑重点的深入看就可以了。

2、《Agile Java》中文版

这本书是出版社送给我的,我一拿到就束之高阁,放在书柜一页都没有翻过,但是前两天整理书柜的时候,拿出来一翻,竟然发现这绝对是一本好书!这本书一大特点是以单元测试和TDD来贯穿全书的,在教你Java各种重要的基础知识的过程中,潜移默化的影响你的编程思维走向敏捷,走向TDD。另外这本书成书很新,以JDK5.0的语法为基础讲解,要学习JDK5.0的新语法也不错。还有这本书对于内容取舍也非常得当,Java语言毕竟类库庞大,可以讲的内容太多,这本书选择的内容以及内容的多寡都很得当,可以让你以最少的时间掌握Java最重要的知识,顺便培养出来优秀的编程思路,真是一本不可多得的好书。

虽然作者自己把这本书定位在入门级别,但我不确定这本书用来入门是不是稍微深了点,我自己也准备有空的时候翻翻这本书,学习学习。

二、Java编程进阶类

打下一个良好的Java基础,还需要更多的实践经验积累,我想没有什么捷径。有两本书值得你在编程生涯的这个阶段阅读,培养良好的编程习惯,提高你的代码质量。

1、《重构 改善既有代码的设计》

这本书名气很大,不用多介绍,可以在闲暇的时候多翻翻,多和自己的实践相互印证。这本书对你产生影响是潜移默化的。

2、《测试驱动开发 by Example》

本书最大特点是很薄,看起来没有什么负担。你可以找一个周末的下午,一边看,一边照做,一个下午就把书看完,这本书的所有例子跑完了。这本书的作用是通过实战让你培养TDD的思路。

三、Java架构师之路

到这个阶段,你应该已经非常娴熟的运用Java编程,而且有了一个良好的编程思路和习惯了,但是你可能还缺乏对应用软件整体架构的把握,现在就是你迈向架构师的第一步。

1、《Expert One-on-One J2EE Design and Development》

这本书是Rod Johnson的成名著作,非常经典,从这本书中的代码诞生了springframework。但是好像这本书没有中译本。

2、《Expert One-on-One J2EE Development without EJB》

这本书由gigix组织翻译,多位业界专家参与,虽然署名译者是JavaEye,其实JavaEye出力不多,实在是忝居译者之名。

以上两本书都是Rod Johnson的经典名著,Java架构师的必读书籍。在我所推荐的这些书籍当中,是我看过的最仔细,最认真的书,我当时读这本书几乎是废寝忘食的一气读完的,有小时候挑灯夜读金庸武侠小说的劲头,书中所讲内容和自己的经验知识一一印证,又被无比精辟的总结出来,读完这本书以后,我有种被打通经脉,功力爆增的感觉。

但是后来我看过一些其他人的评价,似乎阅读体验并没有我那么high,也许是因为每个人的知识积累和经验不同导致的。我那个时候刚好是经验知识积累已经足够丰富,但是还没有系统的整理成型,让这本书一梳理,立刻形成完整的知识体系了。

3、《企业应用架构模式》

Martin的又一本名著,但这本书我只是泛泛的看了一遍,并没有仔细看。这本书似乎更适合做框架的人去看,例如如果你打算自己写一个ORM的话,这本书是一定要看的。但是做应用的人,不看貌似也无所谓,但是如果有空,我还是推荐认真看看,会让你知道框架为什么要这样设计,这样你的层次可以晋升到框架设计者的角度去思考问题。Martin的书我向来都是推崇,但是从来都没有像Rod Johnson的书那样非常认真去看。

4、《敏捷软件开发 原则、模式与实践》

Uncle Bob的名著,敏捷的经典名著,这本书比较特别,与其说是讲软件开发过程的书,不如说讲软件架构的书,本书用了很大篇幅讲各种面向对象软件开发的各种模式,个人以为看了这本书,就不必看GoF的《设计模式》了。

四、软件开发过程

了解软件开发过程不单纯是提高程序员个人的良好编程习惯,也是增强团队协作的基础。

1、《UML精粹》

UML其实和软件开发过程没有什么必然联系,却是软件团队协作沟通,撰写软件文档需要的工具。但是UML真正实用的图不多,看看这本书已经足够了,完全没有必要去啃《UML用户指南》之类的东西。要提醒大家的是,这本书的中译本翻译的非常之烂,建议有条件的看英文原版。

2、《解析极限编程 拥抱变化》XP

这是Kent Beck名著的第二版,中英文对照。没什么好说的,必读书籍。

3、《统一软件开发过程》UP

其实UP和敏捷并不一定冲突,UP也非常强调迭代,测试,但是UP强调的文档和过程驱动却是敏捷所不取的。不管怎么说,UP值得你去读,毕竟在中国真正接受敏捷的企业很少,你还是需要用UP来武装一下自己的,哪怕是披着UP的XP。

4、《敏捷建模》AM

Scott Ambler的名著,这本书非常的progmatic,告诉你怎么既敏捷又UP,把敏捷和UP统一起来了,又提出了很多progmatic的建议和做法。你可以把《解析极限编程 拥抱变化》、《统一软件开发过程》和《敏捷建模》这三本书放在一起读,看XP和UP的不同点,再看AM是怎么统一XP和UP的,把这三种理论融为一炉,形成自己的理论体系,那么你也可以去写书了。

五、软件项目管理

如果你突然被领导提拔为项目经理,而你完全没有项目管理经验,你肯定会心里没底;如果你觉得自己管理项目不善,很想改善你的项目管理能力,那么去考PMP肯定是远水不解近渴的。

1、《快速软件开发》

这也是一本名著。可以这样说,有本书在手,你就有了一个项目管理的高级参谋给你出谋划策,再也不必担心自己不能胜任的问题了。这本书不是讲管理的理论的,在实际的项目管理中,讲这些理论是不解决问题的,这本书有点类似于“软件项目点子大全”之类的东西,列举了种种软件项目当中面临的各种问题,以及应该如何解决问题的点子,你只需要稍加变通,找方抓药就行了。

六、总结

在这份推荐阅读书籍的名单中,我没有列举流行的软件框架类学习书籍,例如Struts,Hibernate,Spring之类,也没有列举AJAX方面的书籍。是因为这类书籍容易过时,而上述的大半书籍的生命周期都足够长,值得你去购买和收藏。

posted @ 2007-10-08 23:08 zibeline 阅读(169) | 评论 (0)编辑 收藏

仅列出标题