存档

文章标签 ‘AMF’

使用Merapi来在AIR和Java中通信

2010年2月2日 ColorHook 没有评论

Merapi是一个用于Java和AIR之间通信的桥接库。通过它可以极大的扩展AIR在本地的执行能力,当然前提是客户端不仅装有AIR运行时虚拟机,还需要安装有Java虚拟机。Merapi通过Socket来通信,使用Adobe的AMF作为数据传输格式。下面看看如何在Flash Builder中创建一个基于Adobe AIR的桌面软件。

在Java和ActionScript两端都存在一名为Bridge的类,它是一个Singleton,用来注册消息类型和发送消息。在这个例子中,AIR向Java端发送一个名为”flashEvent”的消息,消息内容是一个Dictionary实例;并且监听名为”javaEvent”的消息。

<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" 
	layout="absolute" creationComplete="initApp()">
	<mx:Script>
	<![CDATA[
	import demo.JavaMessageHandler;
	import demo.SerializeMessageHandler;
	import merapi.Bridge;
	import merapi.messages.Message;
	import merapi.error.MerapiErrorMessage;
 
	private function initApp():void{
		//register a error handler
		new SerializeMessageHandler();
		//send a message
		var message:Message=new Message();
		message.type="flashEvent";
		var data:Dictionary=new Dictionary();
		data["name"]="colorhook";
		message.data=data;
		message.send();
		//register a message handler
		Bridge.getInstance().registerMessageHandler("javaEvent",
					new JavaMessageHandler());
	}
	]]>
	</mx:Script>
</mx:WindowedApplication>

CoreErrorHandler继承自EventDispatcher,实现了IMessageHandler接口,它在构造函数中就集成了错误处理。所以不需要显示的在Bridge中注册,而是构造一个实例就行了。SerializeMessageHandler继承自CoreErrorHandler,用来处理全局错误。

package demo{
 
	import merapi.handlers.mxml.CoreErrorHandler;	
	import mx.rpc.events.ResultEvent;
 
	public class SerializeMessageHandler extends CoreErrorHandler{
 
		public function SerializeMessageHandler(type:String=null){
			super(type);
			this.addEventListener(ResultEvent.RESULT,onMessageResult);
		}
 
		private function onMessageResult(event:ResultEvent):void{
			trace("SerializeMessageHandler:",event);
		}
	}
}

JavaMessageHandler实现了接口IMessageHandler,并在handleMessage方法中输出从Java中返回的数据。

package demo{
 
	import merapi.handlers.IMessageHandler;
	import merapi.messages.IMessage;
 
	public class JavaMessageHandler implements IMessageHandler{
 
		public function handleMessage(message:IMessage):void{
			trace(message.type);
			trace(message.data);
		}
	}
}

编译运行会发现控制台输出SerializeMessageHandler中的trace信息,因为目前还没有开启Java端的ServerSocket。所以下面来启动一个Java程序。首先调用Bridge.open()来启动ServerSocket。然后监听名为”flashEvent”的消息。

package demo;
 
import org.apache.log4j.Logger;
import merapi.Bridge;
 
public class Application {
 
	protected Logger logger=Logger.getLogger(Application.class);
 
	public Application(){
		logger.info("Application start");
		//open Bridge
		Bridge.open();
		//register a message handler
		Bridge.getInstance().registerMessageHandler("flashEvent",
			new AIRMessageHandler());
	}
 
	public static void main(String[] args){
		new Application();
	}
}

在消息处理器AIRMessageHandler中,输出来自Flash的数据,AMF将ActionScript中的Dictionary映射为Java中的Map。接受到数据之后,我们再从Java中发出一个Message,该Message携带了一个List类型的数据,在Flash中,它会被映射成Array类型。

package demo;
 
import java.util.*;
import org.apache.log4j.Logger;
import merapi.handlers.IMessageHandler;
import merapi.messages.IMessage;
import merapi.messages.Message;
 
public class AIRMessageHandler implements IMessageHandler {
 
	protected Logger logger=Logger.getLogger(AIRMessageHandler.class);
 
	@Override
	public void handleMessage(IMessage message) {
		//handle message
		logger.info(message.getType());
		Map<String,String> map=(Map<String, String>)message.getData();
		logger.info(map.get("name"));
		//send a new message
		Message newMessage=new Message();
		newMessage.setType("javaEvent");
		List<String> list=new ArrayList<String>();
		list.add("Flash Player");
		list.add("AIR");
		newMessage.setData(list);
		newMessage.send();
	}
}

Merapi在Java端用到了Spring, log4j和Adobe AMF相关包, 所以运行时要保证项目包含了这些jar包。可以从MerapiGoogle Code中得到这些包。

AIR2的发布,让我们在AIR和Java通信方面有了新的选择: flerry
flerry使用了AIR2的新特性NativeProcess,它直接创建了一个进程了执行Java程序。从其作者的blog上可以了解更多信息: http://www.riaspace.net/2010/01/flerry-flex-java-bridge-for-adobe-air-2-0/

Flex与ColdFusion通信 (part1)

2009年4月10日 ColorHook 没有评论

Flex跟ColdFusion的通信极为简单,而且它们都是基于标签的语法格式。在Flex中以RPC的形式调用ColdFusion程式不需要向AMFPHP那样指定一个gateway,更没有BlazeDS中繁琐的配置文件。

拿个简单的例子来说明到它底有多简单。

1. 编写ColdFusion组件。

首先建立一个ColdFusion组件来供前台调用。该组件有一个方法sayHello,它接受一个String类型的参数并返回一个String类型的结果。保存文件在服务器根目录的flex目录中,命名为HelloColdFusion.cfc。

<cfcomponent>
	<cffunction name="sayHello" access="remote" returntype="string">
		<cfargument name="info" type="string" required="yes">
		<cfreturn "[Hello ColdFusion], #info#">
	</cffunction>
</cfcomponent>
2. 构建Flex客户端

在Flex中声明一个RemoteObject对象把source属性
Flex代码片段

	<mx:RemoteObject id="service" destination="ColdFusion" 
			source="flex.HelloColdFusion"
			result="Alert.show(String(event.result))"/>
	<mx:Button label="send" click="service2.sayHello('hoho~')"/>
3. 运行程序

点击按钮则弹出服务器的返回消息。

分类: Develop & Design 标签: , ,

使用Spring来配置RemoteObject

2009年4月9日 ColorHook 没有评论

在Flex中使用RemoteObject可以很容易地对Java对象进行RPC调用,但是其配置较为复杂。使用AMFPHP对PHP进行RPC调用只需要指定RemoteObject的destination和endpoint属性即可,而在BlazeDS环境下,需要创建ChannelSet和Channel对象。

通常的做法是使用一个xml文件来配置一个Java destination,Flex在编译的时候会根据配置信息来创建一个完整的RemoteObject对象。但是如果需要更改该RemoteObject对象的destination或者ChannelSet等属性,则需要重新编译,这样的话很是麻烦。

Spring的IOC容器可以很好解决这个问题,它把对象的创建方式声明在外部,然后由容器来动态生成。

使用我的Spring框架来实现这个想法。

1.创建远程的Java对象,编译成class文件放在文件夹WEB-INF\classes\demo下面。

package demo;
public class HelloBlazeDS{
 
	public HelloBlazeDS(){
	}
 
	public String sayHello(String info){
		return "[BlazeDS return] : "+info;
	}
}

2.配置BalzeDS。
打开文件WEB-INF\flex\remoting-config.xml,向其中添加一个destination定义。

<destination id="HelloBlazeDS">
	<properties>
		<source>demo.HelloBlazeDS</source>
	</properties>
</destination>

3.创建Spring的配置文件spring_config.xml。

<?xml version="1.0" encoding="utf-8"?>
<spring-config>
	<beans>
		<bean id="remoteBean" class="mx.rpc.remoting.mxml.RemoteObject">
			<property name="destination" value="HelloBlazeDS"/>
			<property name="channelSet" ref="channelSet"/>
		</bean>
 
		<bean id="channelSet" class="mx.messaging.ChannelSet">
			<method name="addChannel">
				<method-arg ref="channel"/>
			</method>
		</bean>
 
		<bean id="channel" class="mx.messaging.channels.AMFChannel">
			<property name="id" value="my-amf"/>
			<property name="uri" value="http://localhost:8080/blazeds/messagebroker/amf"/>
		</bean>
	</beans>
</spring-config>

该配置文件声明了一个Bean名字叫remoteBean,它是一个RemoteObject对象,这个Bean还引用了channelSet和channel这两个Bean。

4.在Flex中使用Spring,下面是代码片段。

//导入加载配置文件的类ContextLoader
import com.colorhook.spring.context.ContextLoader;
 
private var contextLoader:ContextLoader;
private var remoteService:RemoteObject;
 
//初始化时加载配置文件
private function init():void{
	contextLoader=new ContextLoader();
	contextLoader.addEventListener("complete",onContextLoaderComplete);
}
 
//加载完成之后创建RemoteObject,并调用远程方法sayHello
private function onContextLoaderComplete(event:Event):void{
	contextLoader.removeEventListener("complete",onContextLoaderComplete);
	remoteService=contextLoader.contextInfo.getBean("remoteBean");
	remoteService.sayHello.addEventListener(ResultEvent.RESULT,onSayHelloResult);
	remoteService.sayHello("Spring and BlazeDS");
}
//输出返回结果
private function onSayHelloResult(event:ResultEvent):void{
	Alert.show(String(event.result));
}
分类: Develop & Design 标签: , , , ,

BlazeDS开篇-part2

2009年4月1日 ColorHook 没有评论
Protocols, channels, destinations和endpoints

Data Services可以使用HTTP,HTTPS协议来进行通信,而我们更推崇的是使用开源的AMF协议,AMF协议是基于HTTP协议的,它没有使用特定的端口,所以不需要对防火墙进行特定的设置。更重要的是,AMF使用二进制数据通信,极大的提高了通信效率。

Channel代表一种通信特征。铁轨是给火车跑的,公路给汽车跑。公路也分是普通的、高速的。Channel也是这样,有的Channel用于HTTPService,有用于RemoteService,也可以设置它的参数来改变Channel的行为。

这是一个Channel的定义:

<destination>
<channel-definition id="my-http-longpoll" class="mx.messaging.channels.HTTPChannel">
	<endpoint url="http://servername:8700/contextroot/messagebroker/myhttplongpoll"
				  class="flex.messaging.endpoings.HTTPEndpoint"/>
	<properties>
		<polling-enabled>true</polling-enabled>
		<polling-interval-seconds>0</polling-interval-seconds>
		<wait-interval-millis>60000</wait-interval-millis>
	</properties>
</channel-definition>
</destination>

该Channel是一个HTTPChannel,Channel有一个Endpoint,该Channel的Endpoint是一个HTTPEndpoint,Endpoint声明了一个URI,一个端口和一个Endpoint类型。除了Endpoint,该Channel还有一些参数来配置它的具体行为,这些参数是可选的。

destination是在Flex客户端需要定义的标记,如果你使用的是HTTPService或者WebService,这个destination可以忽略,但是如果你使用RemoteObject来交互,就必须要定义它的destination。

<mx:RemoteObject  id="remoteService"  destination="helloDestination">
</mx:RemoteObject>

上面的Flex代码声明了一个RemoteObject,他的destination名为helloDestination。这个destination到底在什么地方呢?在服务器端的配置文件中。在WEB-INF文件夹下有一个flex文件夹,在flex文件夹中存在4个xml配置文件:services-config.xml,proxy-config.xml,remoting-config.xml,messaging-config.xml。其中services-config.xml被web.xml引用,其它三个被services-config.xml引用。在实际应用中,我们把HTTPService和WebService的destination定义在proxy-config.xml中,把RemoteObject的destination定义在remoting-config.xml中,把Producer和Consumer的destination定义在messaging-config.xm中。

这是一个RemoteObject的destination描述:

<destination id="helloDestination" channels="my-amf">
		<properties>
			<source>demo.HelloBlazeDS</source>
		</properties>
	</destination>

这个destination使用id为my-amf的Channel,对应的远程Java类为demo.HelloBlazeDS。

分类: Develop & Design 标签: ,

BlazeDS开篇-part1

2009年4月1日 ColorHook 没有评论

BlazeDS是Adobe的一款开源免费的Data Services。Data Services是一种遵守AMF或者其它协议(如RTMP)来进行Remoting和传输消息(Messaging)的产品(Product)。

这里是几个Data Services:

 

Data Services的特点如图

 

dataservices

 

安装BlazeDS

Adobe官方网站提供BlazeDS的下载,这是一个turnkey版本,集成了tomcat服务器,下载之后立马就可以享受到它的特性。

分类: Develop & Design 标签: ,

使用AMFPHP传递自定义类

2009年3月26日 ColorHook 没有评论

客户端与服务器使用AMFPHP通信时不仅可以直接传递数字,字符,数组等基本数据类型外,还可以传递更复杂的数据类型。这意味着你可以传递
自定义类的实例。


1.在客户端有个名为ValueObject的AS3类。

该类有id,value和description三个属性。[RemoteClass(alias="")]元标签用来注册该类,服务器就可以根据注册名来查找对应的服务器类。该元标签必须要有,但是别名alias可以任意,不过建议写成完整的限定名。

package demo
{
	[Bindable]
	[RemoteClass(alias="demo.ValueObject")]
	public class ValueObject
	{	
		public var id:String;
		public var value:String;
		[Transient]
		public var description:String;
 
		public function ValueObject()
		{
		}
	}
}


2.在服务器端对应有一个名为ValueObject的PHP类。

该类也有有id, value和description三个属性。该类中有个属性$_explicitType用来和客户端的类对应起来。如果你使用的版本是PHP5,那么这个属性其实是多余的。

<?php
class ValueObject{
	var $_explicitType="demo.ValueObject"; 
 
	public $id;
	public $value;
	public $description;
	function ValueObject(){
	}
	function init($r){
		$keys=array_keys($r);
		foreach($keys as $k){
			$this->$k=$r[$k];
		}
	}
}
?>


3.在AMFPHP的services目录下建立一个服务类ClassMapService。

该类有一个方法updateMyObject,用来接受客户端的自定义类型,并且返回一个服务器端的自定义类型。遗憾的是,客户端的自定义类型没有自动转换成服务器的自定义类型,我们需要手动实现它,PHP类ValueObject有个init方法就是为了达到这个目的。

<?php
require_once "demo/ValueObject.php";
class ClassMapService{
	function updateMyObject($vo){
		$result=new ValueObject();
		$result->init($vo);
		$result->id="PHP:".$result->id;
		$result->value="PHP:".$result->value;
		$result->description="PHP:".$result->description;
		return $result;
	}
}
?>
4.创建一个客户端应用程序来跟ClassMapService交互。
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" 
	 			layout="vertical"
	 			verticalCenter="0" 	
	 			creationComplete="init()">	
	<mx:Style>
		Label{
			fontSize:12px;
			color:#000000;
		}
		Panel{
			padding-left:10;
			padding-right:10;
			padding-bottom:5;
			padding-top:5;
		}
		Label{
			 textAlign:right;
		}
	</mx:Style>
	<mx:Script>
		<![CDATA[
			import demo.ValueObject;
			import mx.rpc.events.ResultEvent;
			import mx.rpc.events.FaultEvent;
			import mx.controls.Alert;
 
			[Bindable]
			private var myValueObject:ValueObject=new ValueObject();
 
			private function init():void{
				remoteService.updateMyObject.addEventListener(ResultEvent.RESULT,
                                                    onUpdateResult);
			}
 
			private function remoteFaultHandler(event:FaultEvent):void{
				Alert.show(event.message.toString());
			}
			private function buttonClickHandler():void{
				var vo:ValueObject=new ValueObject;
				vo.id=id_send.text;	
				vo.value=value_send.text;	
				vo.description=description_send.text;	
				remoteService.updateMyObject.send(vo);
			}
			private function onUpdateResult(e:ResultEvent):void{	
				myValueObject=e.result as ValueObject;
			}
		]]>
	</mx:Script>
	<mx:RemoteObject id="remoteService" 
				destination="php_destination" 
				endpoint="http://localhost:8001/amfphp/gateway.php"
				source="ClassMapService"
				fault="remoteFaultHandler(event)"/>		
 
			<mx:Panel title="ValueObject to send:" width="300">
				<mx:HBox>
					<mx:Label text="id:" width="80"/>
					<mx:TextInput id="id_send"/>
				</mx:HBox>	
				<mx:HBox>
					<mx:Label text="value:" width="80"/>
					<mx:TextInput id="value_send"/>	
				</mx:HBox>
				<mx:HBox>
					<mx:Label text="description:" width="80" />
					<mx:TextInput id="description_send"/>
				</mx:HBox>
				<mx:HBox>
					<mx:Spacer width="180"/>
					<mx:Button label="send" click="buttonClickHandler()"
							width="60"/>
				</mx:HBox>
			</mx:Panel>
 
			<mx:Panel title="ValueObject form PHP:" width="300">
				<mx:HBox>
					<mx:Label text="id:" width="80"/>
					<mx:TextInput id="id_get" text="{myValueObject.id}" 
							editable="false" width="170"/>	
				</mx:HBox>	
				<mx:HBox>
					<mx:Label text="value:" width="80"/>
					<mx:TextInput id="value_get" text="{myValueObject.value}" 
							editable="false" width="170"/>	
				</mx:HBox>
				<mx:HBox>
					<mx:Label text="description:" width="80"/>
					<mx:TextInput id="description_get" 
							text="{myValueObject.description}" 
							width="170" editable="false"/>
				</mx:HBox>
			</mx:Panel>		
</mx:Application>


5.运行程序。

可以发现返回的数据中id和value都包含了输入的数据,而description属性比较不同,原因是客户端的ValueObject类中还有个一个元标签[Transient]。该标签的作用就是向后台传送数据时忽略这个属性的值。如果你的需求不需要交互某个属性,就可以将它设置为[Transient]来减少数据传输。

remotecustomclassresult

一步一步之AMFPHP(3)

2009年3月17日 ColorHook 没有评论

Flex中有RemoteObject类来实现Remoting,这个类无法在Flash IDE中使用。要在Flash CS3中实现Remoting,我们可以使用Flash原生API NetConnection,这里我借助bytearray.org上面提供的包装类来完成。你可以在这个地址中下载相关的类文件和范例:http://www.bytearray.org/?p=122

在这里,我将再次使用上一例子中的HelloRemoting.php这个文件,剩下的就只有建立Flash项目了。

import org.bytearray.remoting.Service;
import org.bytearray.remoting.PendingCall;
import org.bytearray.remoting.events.FaultEvent;
import org.bytearray.remoting.events.ResultEvent;
 
var service:Service=new Service("HelloRemoting",
         "http://localhost:8001/amfphp/gateway.php");
 
var pendingCall:PendingCall=service.sayHello("remoting");
pendingCall.addEventListener(ResultEvent.RESULT,onResult);
pendingCall.addEventListener(FaultEvent.FAULT,onFault);
 
function onResult(e:ResultEvent):void{
	trace(e.result);
}
 
function onFault(e:FaultEvent):void{
	trace(e.fault);
}
运行结果:

hello,remoting

分类: Develop & Design 标签: , , ,