#
public String inputStream2String(InputStream is) throws UnsupportedEncodingException { BufferedReader reader = new BufferedReader(new InputStreamReader(is, "utf-8")); StringBuffer sb = new StringBuffer(); String line = null; try { while ((line = reader.readLine()) != null) { sb.append(line + "\n"); } } catch (IOException e) { e.printStackTrace(); } finally { try { is.close(); } catch (IOException e) { e.printStackTrace(); } } return sb.toString(); }
import java.io.*;
public class Test {
public BufferedReader bufread;
public BufferedWriter bufwriter;
File writefile;
String filepath, filecontent, read;
String readStr = "";
public String readfile(String path) // 从文本文件中读取内容
{
try {
filepath = path; // 得到文本文件的路径
File file = new File(filepath);
FileReader fileread = new FileReader(file);
bufread = new BufferedReader(fileread);
while ((read = bufread.readLine()) != null) {
readStr = readStr + read;
}
} catch (Exception d) {
System.out.println(d.getMessage());
}
return readStr; // 返回从文本文件中读取内容
}
// 向文本文件中写入内容
public void writefile(String path, String content, boolean append) {
try {
boolean addStr = append; // 通过这个对象来判断是否向文本文件中追加内容
filepath = path; // 得到文本文件的路径
filecontent = content; // 需要写入的内容
writefile = new File(filepath);
if (writefile.exists() == false) // 如果文本文件不存在则创建它
{
writefile.createNewFile();
writefile = new File(filepath); // 重新实例化
}
FileWriter filewriter = new FileWriter(writefile, addStr);
bufwriter = new BufferedWriter(filewriter);
filewriter.write(filecontent);
filewriter.flush();
} catch (Exception d) {
System.out.println(d.getMessage());
}
}
}
我们在用maven和eclipse开发WEB应用的时候,需要把servlet-api和jsp-api加入进来,要不然编译不会通过,加入进来之后在打包的时候maven自动把所有的依赖包都放到lib下面,如果你在tomcat下面运行就会有问题,因为tomcat发现你的web应用的lib中包含了servlet-api,他会报错。
解决这个问题的方法就是使用<scope>标签,如下
<dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> <version>2.4</version> <scope>provided</scope> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>jsp-api</artifactId> <version>2.0</version> <scope>provided</scope> </dependency>
这里的意思是说,编译的时候用到servlet-api和jsp-api,但在打包的时候不用这两个依赖。
在maven的官方中是这样描述的
Dependency Scope Dependency scope is used to limit the transitivity of a depedency, and also to affect the classpath used for various build tasks.
There are 6 scopes available:
compile This is the default scope, used if none is specified. Compile dependencies are available in all classpaths of a project. Furthermore, those dependencies are propagated to dependent projects. provided This is much like compile, but indicates you expect the JDK or a container to provide the dependency at runtime. For example, when building a web application for the Java Enterprise Edition, you would set the dependency on the Servlet API and related Java EE APIs to scope provided because the web container provides those classes. This scope is only available on the compilation and test classpath, and is not transitive. runtime This scope indicates that the dependency is not required for compilation, but is for execution. It is in the runtime and test classpaths, but not the compile classpath. test This scope indicates that the dependency is not required for normal use of the application, and is only available for the test compilation and execution phases. system This scope is similar to provided except that you have to provide the JAR which contains it explicitly. The artifact is always available and is not looked up in a repository. import (only available in Maven 2.0.9 or later) This scope is only used on a dependency of type pom in the <dependencyManagement> section. It indicates that the specified POM should be replaced with the dependencies in that POM's <dependencyManagement> section. Since they are replaced, dependencies with a scope of import do not actually participate in limiting the transitivity of a dependency. 如果你这样做了,但使用eclipse+tomcat做测试的时候发现servlet-api还是被打包到lib下面了,你要把maven插件中的WTP也安装一下,问题应该就解决了。
摘要: Flex 中的类体系结构
Flex 包含大量的类,因而无法在本文中全部描述。不过,仔细查看一些类对了解 Flex 对象模型是非常有用的。图 1 显示了核心的类体系结构:图 1. Flex 的核心类体系结构
最顶层的类是 DisplayObject,Flex 将它添加到 Flash Player 的 Stage 对象或通用显示列表。InteractiveObject 处理用户交互,包... 阅读全文
动态按钮
动态按钮的的概念就是在鼠标悬停、鼠标点击、鼠标离开控件时,组件呈现不同的外观,这三种不同的外观可以通过背景位图图片,背景 SWF 文件实现。
位图实现
位图实现是将二进制图像文件作为控件背景,这里使用 Flex 的 Button 控件作为例子,通过定义 Button 的 upSkin、overSkin 以及 downSkin 样式分别设置鼠标离开 Button 时、鼠标悬停 Button 上方时以及鼠标点击 Button 时的皮肤,皮肤制作则使用 Embed 标签加载图片,使之成为 Class 对象,通过 Button 的 setStyle 方法将 Class 对象作为皮肤设置进去,清单 1 展示了其中设置 upSkin 的方法 .downSkin 和 overSkin 的设置方法相同。 清单 1. Button 控件设置 upSkin
var btn:Button= … ;
[Embed(source="images/up.jpg",
scaleGridTop="26",
scaleGridBottom="64",
scaleGridLeft="30",
scaleGridRight="106")]
private var upSkin:Class;
btn.setStyle("upSkin",upSkin);
|
清单 1 中,Embed 标签内的 scaleGridTop、scaleGridBottom、scaleGridLeft、scaleGridRight 属性涉及到一种 Web 技术,名为九宫格的缩放技术,为了解释该技术,请先看图 1. 图 1. 九宫格原理图
注:原图是,5 * 5 方格图,每个方格 4 个像素,上图是被放大后的效果
图 1 在四条红线的位置把图片块成了 9 块,四个角,四条边和中间一块,在缩放的时候,四个角始终不变,两条横向边只缩放宽度,高度不变,两条纵向的边只缩放高度,宽度不变,中间一块宽和高同时缩放,这就是九宫格缩放的原理。这种技术一般是在使用图片做控件的皮肤时使用,很多控件的皮肤样式,在四个角的为位置是圆的或不规则的,所以使用这种缩放技术可以保证控件与图片的大小不一致的时候,图片看起来并不变形。
了解了九宫格技术再看 scaleGridTop、scaleGridBottom、scaleGridLeft、scaleGridRight 四个属性,它们分别代表图 1 中四条红色切割线的位置,scaleGridTop 代表横向顶部的切割线距离图像顶部的像素距离;scaleGridBottom 代表横向底部的切割线距离图像顶部的像素距离;scaleGridLeft 代表纵向左部的切割线距离图像左部的像素距离;scaleGridRight 代表纵向右部的切割线距离图像左部的像素距离。
通过上述技术和方法的使用,具体实例效果展示如图 2. 从左至右分别是 upSkin、downSkin、overSkin 的效果。 图 2. 位图实现效果展示图
SWF 文件实现
SWF 文件实现就是用 SWF 文件中具体的某一帧(MoiveClip)作为控件皮肤,修改 位图实现部分的 Embed 标签,构建的新的示例,如清单 2. 清单 2. 使用 SWF 具体某帧作为皮肤
<mx:Style>
Button{
up-skin:Embed(source="images/bg.swf",symbol="btnUP");
over-skin:Embed(source="images/bg.swf",symbol="btnOVER");
down-skin:Embed(source="images/bg.swf",symbol="btnDOWN");
}
</mx:Style>
<mx:Button id="btn" label="Hello World" width="100" height="60"/>
|
上面代码 Embed 中的 source 指向了一个 SWF 文件,没有与九宫格相关的布局属性,出现了新的属性:symbol,它是 SWF 文件中某一帧的标识符,意思是使用指定的帧作为皮肤。清单 2 的效果如图 3. 从左至右分别是 upSkin、downSkin、overSkin 的效果。 图 3. SWF 实现效果展示图
这种特殊的 SWF 文件需要用到 Flash 开发工具,具体的开发方法读者可以查阅相关的 Flash 资料。
冒泡信息提示框
冒泡信息提示框是自定义的 ToolTip,传统的 Flex ToolTip 效果如图 4 所示。 图 4. 传统 ToolTip
可以看到效果还是比较简陋的,为了增强用户体验,文章实现了新的 ToolTip 让其拥有冒泡效果,效果展示如图 5. 图 5. 冒泡 ToolTip
冒泡 ToolTip 的思想是新建名为 BubbleToolTip 的类,继承于 Canvas,实现 IToolTip 接口,IToolTip 是自定义 ToolTip 必须实现的接口。重写 BubbleToolTip 的 updateDisplayList 方法,目的是重新绘画 BubbleToolTip 的皮肤,皮肤的绘制依赖 BubbleToolTip 内部的 Graphics 对象,通过 this.graphics 语句可以获得该 Graphics 对象的引用,调用 Graphics 对象的 drawRoundRect 方法绘制冒泡的圆形,提示的具体内容则使用子对象 Canvas 存放。清单 3 是 BubbleToolTip 的 updateDisplayList 方法代码。 清单 3. BubbleToolTip 的 updateDisplayList 方法
override protected function updateDisplayList(
unscaledWidth:Number, unscaledHeight:Number ) : void
{
super.updateDisplayList( unscaledWidth, unscaledHeight );
this.graphics.clear();
this.graphics.beginFill( getStyle('backgroundColor'), 1 );
this.graphics.lineStyle(2, getStyle('borderColor'), 1);
this.graphics.drawRoundRect(0, 35, 6, 6, 24, 24);
this.graphics.endFill();
this.graphics.beginFill( getStyle('backgroundColor'), 1 );
this.graphics.lineStyle(2, getStyle('borderColor'), 1);
this.graphics.drawRoundRect(10, 25, 15, 15, 24, 24);
this.graphics.endFill();
}
|
清单 3 的作用是绘画图 5 中黑色的两个圆圈,显示 Hello 信息的则是使用 Canvas 组件,该 Canvas 是在主 MXML 文件中定义,代码如清单 4 所示。 清单 4. 使用 BubbleToolTip 的 MXML 文件
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute"
xmlns:component="component.*" backgroundGradientAlphas="[1.0, 1.0]"
backgroundGradientColors="[#FFFFFF, #FFFFFF]">
<mx:Script><![CDATA[
import mx.events.ToolTipEvent;
import component.BubbleToolTip;
private function createCustomTip(title:String,event:ToolTipEvent):void {
var bt:BubbleToolTip = new BubbleToolTip();
bt.text=title;
content.text = title;
bt.addChild(ppp);
ppp.visible=true;
event.toolTip = bt;
}
private function positionTip(event:ToolTipEvent):void{
event.toolTip.x=event.currentTarget.x + event.currentTarget.width + 10;
event.toolTip.y=event.currentTarget.y;
}
]]></mx:Script>
<mx:Button id="b1"
label="Hello world" x="60" y="60"
toolTip=" "
toolTipCreate="createCustomTip('Hello World',event)"
toolTipShow="positionTip(event)"
/>
<mx:Canvas id="ppp" visible="false" width="50" height="40" cornerRadius="15"
borderColor="#000000" x="35" y="0" borderStyle="solid" borderThickness="2">
<mx:Text id= "content" text="xixi" x="4.5" y="5" width="40" height="23.5">
</mx:Text>
</mx:Canvas>
</mx:Application>
|
清单 4 中定义了存放信息的 Canvas ,id 为 ppp,首先设置 ppp 的 visible 为 false,不可见,在显示 ToolTip 的时候将 ppp 加到 BubbleToolTip 中,并且设置 ppp 为可见。设置 event.tooltip 为指定的 BubbleToolTip 对象。在 createCutomTip 方法中设置新的 ToolTip,在 positionTip 方法中定义 tooltip 的显示位置。
为了形象的表现出重写 updateDisplayList 方法前后的 BubbleToolTip 的对比,图 5显示的是重写后弹出的信息提示框,图 6 是不重写 updateDisplayList 的信息提示框,从两张图片可以看出重写后发生的变化。 图 6. 不重写 updateDisplayList 方法的 BubbleToolTip
可以看到子对象的 Canvas 前后不变,因为并没有重写它的 updateDisplayList,而重写 BubbleToolTip 的 updateDisplayList 方法后边框变成了两个冒泡。注意 BubbleToolTip 对象实际上也是一个 Canvas。
网格背景
网格背景在一些 Flex 流程编辑器中可以看到,画布的网格背景不仅可以增强用户体验,对于流程编辑中的流程节点的对齐也有很大的帮助。
网格背景的容器使用画布 Canvas 对象,使用 CSS 重新定义容器的皮肤。定义新皮肤的 CSS 代码如清单 5. 清单 5. CSS 定义新皮肤 Canvas
{
borderSkin: ClassReference("skins.GridSkin");
}
|
skins .GridSkin 是一个 Flex 类,继承于 ProgrammaticSkin,ProgrammaticSkin 类代表编程式的皮肤定义,重写它的 updateDisplayList 方法可以设置新的皮肤,这和直接重写 Canvas 的 updateDisplayList 方法效果上是一致的,本文重在讲解新的定义方法,因此使用 CSS 定义。清单 6 显示网格定义的代码。 清单 6. ProgrammaticSkin 的 updateDisplayList 实现代码
override protected function updateDisplayList(unscaledWidth:Number,
unscaledHeight:Number):void {
var g:Graphics = this.graphics;
g.clear();
g.beginFill(0x000000, 0);
g.drawRect(0, 0, unscaledWidth, unscaledHeight);
g.endFill();
g.lineStyle(1, 0x000000, 0.1);
var squareSize:Number = 15;
var numRows:Number = unscaledHeight / squareSize;
var numCols:Number = unscaledWidth / squareSize;
for(var row:Number = 0; row< numRows; row++) {
g.moveTo(0, row * squareSize);
g.lineTo(unscaledWidth, row * squareSize);
}
for(var col:Number = 0; col< numCols; col++) {
g.moveTo(col * squareSize, 0);
g.lineTo(col * squareSize, unscaledHeight);
}
}
|
清单 6 中的 unscaledWidth 和 unscaledHeight 代表了 Canvas 画布实际的宽和高,画布内的 Graphics 对象默认的绘画原点是画布左上角(0,0)点。代码 drawRect(0, 0, unscaledWidth, unscaledHeight) 用于绘画整个画布的边框,lineStyle(1, 0x000000, 0.1) 设置将要画的线的样式,黑色、透明度 0.1、粗细 1,squareSize 定义网格的大小(网格为正方形)。通过 unscaledHeight / squareSize 和 unscaledWidth / squareSize 获得画布允许的行数和列数,moveTo 移动绘画原点,lineTo 表示从原点到目标点画线。For 循环代表每画一行(列),原点向下(右)移动 squareSize 的距离,再从原点平行画直线到画布右边缘(底边缘)。那么网格效果就出来了 . 效果如图 7 所示 . 图 7. 网格背景效果
图像圆角
图像圆角是 Web2.0 重要的标志,如果你用过 CSS 进行圆角的开发,那么不管在开发过程中或者处理浏览器兼容问题时一定会大喊痛苦。然而在 Flex 中,图像圆角变的非常简单,使用 Mask 技术就可以轻松得实现。这种技术的直观感觉是将某一控件 A 覆盖在另一控件 B 之上,而 B 显示的部分则是被 A 遮盖的部分。如果 A 具有透明度,那么 B 也会有透明度。例子代码如清单 7. 所示。 清单 7. 图像圆角组件
<?xml version="1.0" encoding="utf-8"?>
<mx:Canvas
xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:filters="flash.filters.*"
width="100%" height="100%" >
<mx:Image source="assets/ygy.jpg" x="10" y="10" mask="{maskCanvas}">
<mx:filters>
<filters:DropShadowFilter />
</mx:filters>
</mx:Image>
<mx:Canvas x="81" y="62" width="344" height="274" backgroundColor="#ff0000"
id="maskCanvas" cornerRadius="15" borderStyle="solid"/>
</mx:Canvas>
|
清单 7 中图像圆角组件实际上是一个 Canvas,该组件内显示的是一个 Image(A),该 Image 指定 mask 为 maskCanvas,也就是一个 id 为 maskCanvas 的 Canvas(B),代码的意思是使用 B 遮盖 A,那么 B 的样式决定了 A 显示的样式,B 的样式中重要的是 backgroundColor 和 cornerRadius,backgroundColor 设置成红色,并不透明,cornerRadius 设置 B 的圆角(注意,Image 没有这个属性),那么使用深色的 B 遮盖 A,A 将显示被 B 遮盖的部分,也就是显示了图像的圆角,效果如图 8。 图 8. 图像圆角效果
该效果使用的原图像如图 9 所示。这是一张方形的图像。 图 9. 原图像
从清单 7 的 Design 视图可以看出 A 和 B 的遮盖结果,效果如图 10。 图 10. Design 视图
原图像主要内容被红色背景的 Canvas 遮盖,四个角没被遮盖因此运行时则不被显示。运行后效果就会如图 8 所示了。
Flex is rapidly becoming the preferred technology for building groundbreaking internet applications delivered in the browser and on the desktop (using the AIR runtime). For several years, Spring has been one of the most popular frameworks for building the Java back-end of internet applications. In this article, we describe how to use BlazeDS Remoting to seamlessly integrate the two technologies and build state-of-the-art internet applications made of a Flex front-end and a Spring back-end.
Whether you are a Flex developer with limited knowledge of Spring, or a Spring developer with limited knowledge of Flex, you can benefit from the powerful integration of these products. This section describes how BlazeDS enables a tight integration between Flex and Spring, and provides background information on the technologies at play.
What is Spring?
Spring is one of the most popular Java frameworks. The foundation of the Spring framework is a lightweight component container that implements the Inversion of Control (IoC) pattern.
Using an IoC container, components don't instantiate or even look up their dependencies (the objects they work with). The container is responsible for injecting those dependencies when it creates the components (hence the term "Dependency Injection" also used to describe this pattern).
The result is looser coupling between components. The Spring IoC container has proven to be a solid foundation for building robust enterprise applications. The components managed by the Spring IoC container are called Spring beans.
The Spring framework includes several other modules in addition to its core IoC container. These modules are not covered in this document even though we will be using the Spring JDBC abstraction framework in the second sample application below. More information on the Spring framework can be found at http://www.springframework.org.
What is Flex?
Flex is an environment for building Rich Internet Applications. The Flex programming model is made of:
- ActionScript, an ECMAScript compliant, object-oriented programming model. With some syntactical differences, ActionScript looks and feels similar to Java, and supports the same object-oriented constructs: packages, classes, inheritance, interfaces, strong (but also dynamic) typing etc.
- MXML: an XML-based language that provides an abstraction on top of ActionScript, and allows parts of an application (typically the View) to be built declaratively.
- An extensive set of class libraries. The documentation is available here in a Javadoc-like format.
The Flex source code (.mxml and .as files) is compiled into Flash bytecode (.swf) that is executed at the client-side by the Flash virtual machine using a Just-In-Time compiler.
The Flex SDK is an open source project. It includes the Flex component library, the compiler, the debugger, and the documentation. A complete discussion of Flex is beyond the scope of this document. You can find more information and download the Flex SDK at http://opensource.adobe.com.
What is BlazeDS?
BlazeDS is a set of data services that give your Flex applications additional options for data connectivity. Without BlazeDS (or, without deploying any Flex-specific component at the server-side), Flex applications can access back-end data using either the HTTPService or the WebService:
- You use the HTTPService component to send HTTP requests to a server, and consume the response. Although the HTTPService is often used to consume XML, it can be used to consume other types of responses. The Flex HTTPService is similar to the XMLHttpRequest component available in Ajax.
- You use the WebService component to invoke SOAP-based web services.
BlazeDS adds the following services:
- The Remoting Service allows your Flex application to directly invoke methods of Java objects deployed in your application server.
- The Message Service provides a publish/subscribe infrastructure that enables your Flex application to publish messages and subscribe to a messaging destination, enabling the development of real-time data push and collaborative applications.
- The Proxy Service allows your Flex application to make cross-domain service requests in a secure and controlled manner. In other words, it allows your Flex application to access a service available on a different domain than the domain from where the application was downloaded (without having to deploy a crossdomain.xml policy file on the target domain).
BlazeDS is deployed as a set of JAR files as part of your web application. Like the Flex SDK, BlazeDS is an open-source project. More information is available at http://opensource.adobe.com.
In this document, we focus on the Remoting service. The Remoting Service provides a tight and natural integration with Spring. There is no need to transform data, or to expose services in a certain way: the Flex application can directly access the beans registered in the Spring IoC container.
How does Flex use BlazeDS to access Spring beans?
So, if Flex clients can remotely access Java objects, and if Spring beans are Java objects, aren't we all set and ready to start accessing Spring beans from Flex clients? Almost… There is one simple element to configure.
The whole idea behind Spring IoC is to let the container instantiate components (and inject their dependencies). By default, however, components accessed remotely by a Flex client are instantiated by BlazeDS on the server. The key to the Flex/Spring integration, therefore, is to configure BlazeDS to let the Spring container take care of instantiating Spring beans. BlazeDS supports the concept of a factory to enable this type of custom component instantiation. The role of a factory is simply to provide ready-to-use instances of components to BlazeDS (instead of letting BlazeDS instantiate these components).
The supporting files available with this article include a factory class (SpringFactory) that provides BlazeDS with fully initialized (dependency-injected) instances of Spring beans. Note: The SpringFactory was developed by Jeff Vroom (Flex Data Services architect) and is also available on Adobe Exchange.
The remainder of this article describes how to configure your web application to use BlazeDS and Spring, how to configure the Spring Factory, and how to put the pieces together and start invoking Spring beans from Flex applications.
Configuring your web application is simple and merely requires installing and registering the necessary components.
Step 1: Install the BlazeDS turnkey server
The BlazeDS turnkey server is a ready-to-use version of Apache Tomcat in which the Blaze data services have already been deployed along with sample applications. The goal of the turnkey server is to give developers an easy way to run samples and tutorials out of the box.
To install the BlazeDS turnkey server:
- Make sure that you have the JDK 1.5 or higher installed, and that you have a JAVA_HOME environment variable pointing to your Java Development Kit installation.
- Download blazeds-turnkey-<version>.zip.
- Unzip blazeds-turnkey-<version>.zip in /blazeds.
Note.You can unzip blazeds-turnkey-<version>.zip anywhere else. Just make sure you adjust the path in the instructions provided in this document accordingly. - Start the samples database.
- Open a command prompt.
- Navigate to /blazeds/sampledb.
- Execute startdb.bat (Windows) or startdb.sh (Unix-based systems)
- Start Tomcat.
- Open a command prompt.
- Navigate to /blazeds/tomcat/bin.
- Execute the following command:
- Access http://localhost:8400/samples to make sure the installation is successful: you should see the BlazeDS samples home page.
As an alternative to using the turnkey server, you could use one of the following options:
- Download blazeds-<version>.war and deploy it in your own application server. Blazeds-<version>.war is a blank web application configured to use the BlazeDS data services.
Note: samples-<version>.war includes everything included in blazeds.war, plus a series of samples. - Download blazeds-<version>.war and merge its content in your own web application.
The instructions in this document assume that you use the BlazeDS turnkey server. If you are using one of the alternative options, you will have to adjust the instructions accordingly.
Step 2: Install the Flex SDK
You need the Flex compiler to compile the client-side (Flex-side) of the sample applications discussed in this document. The Flex compiler is part of the Flex SDK. As a convenience, the Flex SDK is available as part of the BlazeDS installation in blazeds/resources/flex_sdk/flex_sdk_3.zip.
Note: The Flex SDK can also be downloaded at http://opensource.adobe.com. It is also available as part of Flex Builder.
To install the Flex SDK:
You can skip this step if Flex Builder 3 is installed on your system.
Unzip /blazeds/resources/flex_sdk/flex_sdk_3.zip in /flex_sdk_3.
Note: You can unzip flex_sdk_3.zip anywhere else. Just make sure you adjust the path in the instructions provided in this document accordingly.
Step 3: Install Spring
Note: A complete discussion of the Spring installation process is beyond the scope of this article. Refer to http://www.springframework.org for more information. The steps below describe a basic configuration that is sufficient for the purpose of this article.
- Download the Spring framework at http://www.springframework.org/download (the version without dependencies is sufficient to complete the examples in this article).
Note: The examples below have been developed and tested using Spring 2.x. However the integration approach described in this document (and the SpringFactory class) should work fine using Spring 1.2.8 (some of the examples might not work because they use Spring 2.x features). - Unzip the downloaded file.
- Locate spring.jar in the dist directory and copy the file to /blazeds/tomcat/webapps/blazeds/WEB-INF/lib
- Open /blazeds/tomcat/webapps/blazeds/WEB-INF/web.xml and add the context-param and listener definitions as follows:
<context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/applicationContext.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener </listener-class> </listener>
Step 4: Install the supporting files
- Download flex-spring.zip here
- Unzip flex-spring.zip in /flex-spring
Flex-spring.zip includes the Spring factory as well as the supporting files for the examples below.
Step 5: Register the Spring factory
- Copy SpringFactory.class and SpringFactory$SpringFactoryInstance.class from /flex-spring/factory/bin/flex/samples/factories to /blazeds/tomcat/webapps/blazeds/WEB-INF/classes/flex/samples/factories.
- Register the Spring factory in /blazeds/tomcat/webapps/blazeds/WEB-INF/flex/services-config.xml:
<factories> <factory id="spring" class="flex.samples.factories.SpringFactory"/> </factories>
This first application is intentionally simplistic to provide an uncluttered example of wiring Spring beans together and invoking them from a Flex application.
Step 1: Examine the application source code
- Examine the Flex source code:
Open MortgageCalc.mxml located in /flex-spring/samples/mortgage/flex in a code editor to familiarize yourself with the application. The application enables the user to enter a mortgage amount. When the user clicks the "Calculate" button, the application obtains the value of the monthly payment for that mortgage by invoking the calculate() method of the remote object identified by a logical name: "mortgageService". "mortgageService" is mapped to a fully qualified Java class name in the remoting-config.xml file (see Step 3 below). - Examine the Java source code:
Open RateFinder.java, SimpleRateFinder.java and Mortgage.java located in /flex-spring/samples/mortgage/java in a code editor. Notice that Mortgage has a dependency to a RateFinder object. Mortgage doesn't instantiate a RateFinder object itself, doesn't lookup up for a RateFinder object, and doesn't even know the exact type of the object it will be dealing with (RateFinder is an inteface). An instance of a class implementing the RateFinder interface will be injected by the container (using the setRateFinder method) when it instantiates the component (see step 2 below).
Step 2: Register Spring beans
- If it doesn't already exist, create a file named applicationContext.xml in /blazeds/tomcat/webapps/blazeds/WEB-INF.
- Register the rateFinderBean and mortgageBean beans in applicationContext.xml as follows:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"> <beans> <bean id="rateFinderBean" class="flex.samples.spring.mortgage.SimpleRateFinder"/> <bean id="mortgageBean" class="flex.samples.spring.mortgage.Mortgage"> <property name="rateFinder" ref="rateFinderBean"/> </bean> </beans>
Notice that in the mortgageBean definition, we tell the container how to inject the rateFinder dependency: the rateFinder property is mapped to rateFinderBean, which defines an instance of the SimpleRateFinder class.
Step 3: Configure the Flex Remoting destination
- Open remoting-config.xml in /blazeds/tomcat/webapps/blazeds/WEB-INF/flex.
- Add a mortgageService destination as follows:
<destination id="mortgageService"> <properties> <factory>spring</factory> <source>mortgageBean</source> </properties> </destination>
Notice that we use the Spring factory defined above (see "Register the Spring Factory"), and we provide the name of the Spring bean as defined in applicationContext.xml as the source.
Step 4: Build the project
The Flex SDK includes ANT tasks that make it easy to compile Flex applications and create HTML pages to host them as part of a build process. The support files include build scripts to compile and deploy each sample application.
Note: You need the Apache Ant build tool to build and deploy your applications as described in this document. If Apache Ant is not installed on your system, visit http://ant.apache.org and follow the download and installation instructions.
To build the Mortgage Calculator project:
- Edit /flex-spring/samples/mortgage/build.xml:
- Make sure the FLEX_HOME property points to the location of the Flex SDK on your system.
- Make sure the CONTEXT_ROOT property matches the context root of your web application.
- Open a command prompt.
- Navigate to /flex-spring/samples/mortgage.
- Execute the following command to compile and deploy the client and server of the application:
ant
The build process deploys the client side of the application (the compiled flex application) in /blazeds/tomcat/webapps/blazeds/mortgage and the server side (the compiled Java classes) in /blazeds/tomcat/webapps/blazeds/WEB-INF/classes/flex/samples/spring/mortgage.
Step 5: Run the client application
- Restart Tomcat.
- Open a browser, access http://localhost:8400/blazeds/mortgage/index.html, and test the application: Enter a loan amount and click "Calculate" to get the monthly payment for a 30-year mortgage.
This second example is more sophisticated and includes database connectivity. To keep the application simple and avoid dependencies on other products or frameworks, the Spring JDBC abstraction framework is used to access the database. You could use the Spring support for ORM data access (using Hibernate, JDO, Oracle TopLink, iBATIS, or JPA) as an alternative: the specific Spring data access strategy you choose has no impact on the Flex/Spring integration. This sample application has two modules: a database maintenance module (storeadmin) and a customer-facing product catalog with filtering capabilities (store).
Step 1: Examine the application source code
- Examine the Flex source code:
Open store.mxml located in /flex-spring/samples/store/flex in a code editor to familiarize yourself with the store application. Notice the RemoteObject declaration pointing to the "productService" destination. "productService" is mapped to a Spring bean in Step 3 below. When the application starts, it invokes the findAll() method on the remote bean to retrieve the list of products (see the createComplete event on the Application tag).
Open storeadmin.mxml and ProductForm.mxml located in /flex-spring/samples/store/flex in a code editor to familiarize yourself with the storeadmin application. Notice that the storeadmin application uses the same remote Spring bean (productService) as the store application to retrieve the list of products using the findAll() method, and update changes using the updateProduct() method. - Examine the Java source code:
Open ProductDAO.java, SimpleProductDAO.java and Product.java located in /flex-spring/samples/store/java in a code editor. Notice that SimpleProductDAO extends org.springframework.jdbc.core.support.JdbcDaoSupport. JdbcDaoSupport has a dependency to a javax.sql.DataSource object (javax.sql.DataSource is an interface).
Step 2: Register Spring beans
- Register the dataSource and productDAOBean beans in applicationContext.xml as follows:
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="org.hsqldb.jdbcDriver"/> <property name="url" value=" jdbc:hsqldb:hsql://localhost:9002/flexdemodb"/> <property name="username" value="sa"/> <property name="password" value=""/> </bean> <bean id="productDAOBean" class="flex.samples.spring.store.SimpleProductDAO"> <property name="dataSource" ref="dataSource"/> </bean>
- Copy hsqldb.jar from blazeds/tomcat/webapps/samples/WEB-INF/lib to /blazeds/tomcat/webapps/blazeds/WEB-INF/lib.
Step 3: Configure the Flex Remoting destination
- Open remoting-config.xml in /blazeds/tomcat/webapps/blazeds/WEB-INF/flex.
- Add the productService destination as follows:
<destination id="productService"> <properties> <factory>spring</factory> <source>productDAOBean</source> </properties> </destination>
Step 4: Build the project
- Edit /flex-spring/samples/store/build.xml:
- Make sure the FLEX_HOME property points to the location of the Flex SDK on your system.
- Make sure the CONTEXT_ROOT property matches the context root of your web application.
- Open a command prompt.
- Navigate to /flex-spring/samples/store.
- Execute the following command to compile and deploy the client-side and the server side of the application:
ant
The build process deploys the client-side of the store and storeadmin applications in /blazeds/tomcat/webapps/blazeds/store and /blazeds/tomcat/webapps/blazeds/storeadmin respectively, and the server-side (the compiled Java classes) in /blazeds/tomcat/webapps/blazeds/WEB-INF/classes/flex/samples/spring/store.
Step 5: Run the client application
- Restart Tomcat
- Open a browser, access http://localhost:8400/blazeds/storeadmin/index.html, and test the storeadmin application.
- Open a browser, access http://localhost:8400/blazeds/store/index.html, and test the store application.
In my previous tutorial on BlazeDS, I went over an example over how to setup remote objects with data push. There are however, a lot of instances where someone just wanted to receive data and they aren’t concerned with it being updated., so it turns more into a ‘client requesting data whenever it wants it’ scenario. This article will not be nearly as indepth as the last blazeDS article, but if you have not setup your environment for BlazeDS before, it will probably be in your best interest to check it out.
Lets begin by making sure that we have our server and BlazeDs war file. Both of them can be found here:
Tomcat 6 BlazeDS
Once again, we are going to extract the contents into our BlazeDS server project. This time, we are only going to edit remoting-config.xml. Make sure that the following lines are in there.
<default-channels>
<channel ref="my-amf"/>
</default-channels>
<destination id="BlazeDsService">
<properties>
<source>com.codeofdoom.BlazeDsService</source>
</properties>
</destination>
We tell the remoting-config to use the channel “my-amf”, which is already defined with the services-config.xml file. Next we are telling it to use the the destination “BlazeDsService”. This is what our remote object will be looking at. The properties also contains a source. This source is going to be a java class with the methods it needs in order to process the calls from the front end. You must make sure you include the fully qualified name, so we will call it com.codeofdoom.BlazeDsService. So what does this file look like?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
package com.codeofdoom;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import flex.messaging.io.ArrayCollection;
public class BlazeDsService {
private static final String[] MASTER_LIST = {"C", "FNM", "FRE", "F", "GOOG", "AIG", "CSCO", "MSFT", "AAPL", "YHOO", "BSX", "PORT","F", "TNT", "ESP", "RET", "VBN", "EES"};
public BlazeDsService(){}
public List<StockQuote> getQuotes(){
List<StockQuote> list = new ArrayList<StockQuote>();
Random r = new Random();
for (String s:MASTER_LIST){
StockQuote sq = new StockQuote();
sq.setName(s);
sq.setPrice(r.nextInt(50));
list.add(sq);
}
return list;
}
} |
Yep. Thats it. It not implementing anything crazy, not extending any service adapters, anything like that. It’s just a java class with some functions on it. Next we are going to create the object that we will be sending back and forth. In this example, we are going to be simulating retrieving some stock data, so I called the class StockQuote.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
package com.codeofdoom;
public class StockQuote {
private String name;
private int price;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
} |
On the front end, we are going to need the matching object, and here that is.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
package com.codeofdoom.dto
{
[Bindable]
[RemoteClass(alias="com.codeofdoom.StockQuote")]
public class StockQuote{
private var _name:String
private var _price:int;
public function get name():String{
return _name;
}
public function get price():int{
return _price;
}
public function get name(name:String):void{
_name = name;
}
public function get price(price:int):void{
_price = price;
}
}
} |
I mentioned this in the last article, but I am going to mention this again, just to stress the importance. When we are using Remote Object, we must make sure that
- All the properties are the same.
- We have getters and setters for all properties on back the java side and the AS side.
- We must have our AS object set as Bindable and the RemoteClass set to the path of our remote object.
- We must make sure that the constructors match. I have seen where flex will swallow the error message if they don’t match, making debugging a pain.
Now lets take a look at the mxml.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
<mx:Script>
<![CDATA[
import mx.rpc.events.ResultEvent;
import mx.collections.ArrayCollection;
import mx.messaging.messages.IMessage;
import mx.messaging.events.MessageAckEvent;
import mx.messaging.messages.AsyncMessage;
import mx.messaging.events.MessageEvent;
public function retrieveStocks():void{
ro.getQuotes();
}
private function result(e:ResultEvent):void{
stockChart.dataProvider = e.result as ArrayCollection;
}
]]>
</mx:Script>
<mx:RemoteObject id="ro" result="result(event)" destination="BlazeDsService"/>
<mx:VBox width="100%">
<mx:BarChart width="100%" id="stockChart">
<mx:horizontalAxis>
<mx:CategoryAxis categoryField="name"/>
</mx:horizontalAxis>
<mx:series>
<mx:ColumnSeries xField="name" yField="price"/>
</mx:series>
</mx:BarChart>
<mx:Button label="Retrieve Stocks" click="retrieveStocks()"/>
</mx:VBox>
</mx:Application> |
Once again, not much to it. There are three areas of importance.
- Our remote object is that is subscribed to our destination that we declared within our remoting-config.xml.
- The retrieveQuotes function is what calls our backend. Note that it just calls ‘getQuotes’ directly from the remote object.
- The result function that is set on the RemoteObject is what comes back from the back end. Now a lot of examples will do something to the grid such as dataprovider = {ro.getQuotes.lastResult}. We could do the same here, but i wanted to give you a chance to inspect the object to see what exactly is coming back.
Once you click the Retrieve Stocks button, it then calls the getQuotes() function on the remote object. That then builds our List of objects and passes them to the front end. When it comes back, we then just apply it to the dataprovider of our chart.
I really wanted to take the time out to show you some other ways of handling your data within BlazeDS. It is an extremely powerful tool that every Flex developer should have under their belt. Its a lot less cumbersome than standard calls to the back end and the amount of data that is sent back and forth is a lot smaller than sending plane XML over the wire. Good luck!
Here is the source
Java's Collections and Relational database (and thus Hibernate) relies heavily on being able to distinguish objects in a unified way. In Relational database's this is done with primary keys, in Java we have equals() and hashCode() methods on the objects. This page tries to discuss the best strategies for implementation of equals() and hashcode() in your persistent classes.
Why are equals() and hashcode() important Normally, most Java objects provide a built-in equals() and hashCode() based on the object's identity; so each new() object will be different from all others.
This is generally what you want in ordinary Java programming. And if all your objects are in memory, this is a fine model. Hibernate's whole job, of course, is to move your objects out of memory. But Hibernate works hard to prevent you from having to worry about this.
Hibernate uses the Hibernate session to manage this uniqueness. When you create an object with new(), and then save it into a session, Hibernate now knows that whenever you query for an object and find that particular object, Hibernate should return you that instance of the object. And Hibernate will do just that.
However, once you close the Hibernate session, all bets are off. If you keep holding onto an object that you either created or loaded in a Hibernate session that you have now closed, Hibernate has no way to know about those objects. So if you open another session and query for "the same" object, Hibernate will return you a new instance. Hence, if you keep collections of objects around between sessions, you will start to experience odd behavior (duplicate objects in collections, mainly).
The general contract is: if you want to store an object in a List, Map or a Set then it is an requirement that equals and hashCode are implemented so they obey the standard contract as specified in the documentation.
What is the problem after all? So let's say you do want to keep objects around from session to session, e.g. in a Set related to a particular application user or some other scope that spans several Hibernate sessions.
The most natural idea that comes to mind is implementing equals() and hashCode() by comparing the property you mapped as a database identifier (ie. the primary key attribute). This will cause problems, however, for newly created objects, because Hibernate sets the identifier value for you after storing new objects. Each new instance therefore has the same identifier, null (or <literal>0</literal>). For example, if you add some new objects to a Set:
// Suppose UserManager and User are Beans mapped with Hibernate UserManager u = session.load(UserManager.class, id); u.getUserSet().add(new User("newUsername1")); // adds a new Entity with id = null or id = 0 u.getUserSet().add(new User("newUsername2")); // has id=null, too so overwrites last added object. // u.getUserSet() now contains only the second User As you can see relying on database identifier comparison for persistent classes can get you into trouble if you use Hibernate generated ids, because the identifier value won't be set before the object has been saved. The identifier value will be set when session.save() is called on your transient object, making it persistent.
If you use manually assigned ids (e.g. the "assigned" generator), you are not in trouble at all, you just have to make sure to set the identifier value before adding the object to the Set. This is, on the other hand, quite difficult to guarantee in most applications.
Separating object id and business key To avoid this problem we recommend using the "semi"-unique attributes of your persistent class to implement equals() (and hashCode()). Basically you should think of your database identifier as not having business meaning at all (remember, surrogate identifier attributes and automatically generated vales are recommended anyway). The database identifier property should only be an object identifier, and basically should be used by Hibernate only. Of course, you may also use the database identifier as a convenient read-only handle, e.g. to build links in web applications.
Instead of using the database identifier for the equality comparison, you should use a set of properties for equals() that identify your individual objects. For example, if you have an "Item" class and it has a "name" String and "created" Date, I can use both to implement a good equals() method. No need to use the persistent identifier, the so called "business key" is much better. It's a natural key, but this time there is nothing wrong in using it!
The combination of both fields is stable enough for the life duration of the Set containing your Items. It is not as good as a primary key, but it's certainly a candidate key. You can think of this as defining a "relational identity" for your object -- the key fields that would likely be your UNIQUE fields in your relational model, or at least immutable properties of your persistent class (the "created" Date never changes).
In the example above, you could probably use the "username" property.
Note that this is all that you have to know about equals()/hashCode() in most cases. If you read on, you might find solutions that don't work perfectly or suggestions that don't help you much. Use any of the following at your own risk.
Workaround by forcing a save/flush If you really can't get around using the persistent id for equals() / hashCode(), and if you really have to keep objects around from session to session (and hence can't just use the default equals() / hashCode()), you can work around by forcing a save() / flush() after object creation and before insertion into the set:
// Suppose UserManager and User are Beans mapped with Hibernate UserManager u = session.load(UserManager.class, id); User newUser = new User("newUsername1"); // u.getUserSet().add(newUser); // DO NOT ADD TO SET YET! session.save(newUser); session.flush(); // The id is now assigned to the new User object u.getUserSet().add(newUser); // Now OK to add to set. newUser = new User("newUsername2"); session.save(newUser); session.flush(); u.getUserSet().add(newUser); // Now userSet contains both users. Note that it's highly inefficient and thus not recommended. Also note that it is fragile when using disconnected object graphs on a thin client:
// on client, let's assume the UserManager is empty: UserManager u = userManagerSessionBean.load(UserManager.class, id); User newUser = new User("newUsername1"); u.getUserSet().add(newUser); // have to add it to set now since client cannot save it userManagerSessionBean.updateUserManager(u); // on server: UserManagerSessionBean updateUserManager (UserManager u) { // get the first user (this example assumes there's only one) User newUser = (User)u.getUserSet().iterator().next(); session.saveOrUpdate(u); if (!u.getUserSet().contains(newUser)) System.err.println("User set corrupted."); } This will actually print "User set corrupted." since newUser's hashcode will change due to the saveOrUpdate call.
This is all frustrating because Java's object identity seems to map directly to Hibernate-assigned database identity, but in reality the two are different -- and the latter doesn't even exist until an object is saved. The object's identity shouldn't depend on whether it's been saved yet or not, but if your equals() and hashCode() methods use the Hibernate identity, then the object id does change when you save.
It's bothersome to write these methods, can't Hibernate help? Well, the only "helping" hand Hibernate can provide is hbm2java.
hbm2java does not (anymore) generate equals/hashcode based on id's because of the described issues in this page.
You can though mark certain properties with <meta attribute="use-in-equals">true</meta> to tell hbm2java to generate a proper equals/hashcode.
Summary To sum all this stuff up, here is a listing of what will work or won't work with the different ways to handle equals/hashCode:
no eq/hC at all eq/hC with the id property eq/hC with buisness key use in a composite-id No Yes Yes multiple new instances in set Yes No Yes equal to same object from other session No Yes Yes collections intact after saving Yes No Yes Where the various problems are as follows:
use in a composite-id:
To use an object as a composite-id, it has to implement equals/hashCode in some way, == identity will not be enough in this case.
multiple new instances in set:
Will the following work or not:
HashSet someSet = new HashSet(); someSet.add(new PersistentClass()); someSet.add(new PersistentClass()); assert(someSet.size() == 2); equal to same object from another session:
Will the following work or not:
PersistentClass p1 = sessionOne.load(PersistentClass.class, new Integer(1)); PersistentClass p2 = sessionTwo.load(PersistentClass.class, new Integer(1)); assert(p1.equals(p2)); collections intact after saving:
Will the following work or not:
HashSet set = new HashSet(); User u = new User(); set.add(u); session.save(u); assert(set.contains(u)); Any best practicies for equals and hashcode Read the links in 'Background material' and the API docs - they provide the gory details.
Furthermore I encourage anyone with information and tips about equals and hashcode implementations to come forward and show their "patterns" - I might even try to incorporate them inside hbm2java to make it even more helpful ;)
Background material: Effective Java Programming Language Guide, sample chapter about equals() and hashCode()
Java theory and practice: Hashing it out, Article from IBM
Sam Pullara (BEA) comments on object identity: Blog comment
Article about how to implement equals and hashCode correctly by Manish Hatwalne: Equals and HashCode
Forum thread discussing implementation possibilities without defining a business identity: Equals and hashCode: Is there *any* non-broken approach?
According to The java.lang.Object documentation it should be perfectly ok to always return 0 for the hashCode(). The positive effect of implementing hashCode() to return unique numbers for unique objects, is that it might increase performance. The downside is that the behavior of hashCode() must be consistent with equals(). For object a and b, if a.equals(b) is true, than a.hashCode() == b.hashCode() must be true. But if a.equals(b) returns false, a.hashCode() == b.hashCode() may still be true. Implementing hashCode() as 'return 0' meets these criteria, but it will be extremely inefficient in Hash based collection such as a HashSet or HashMap.
Messages
All communication between Flex client components and BlazeDS is performed with messages. Flex components use several message types to communicate with their corresponding services in BlazeDS. All messages have client-side (ActionScript) implementations and server-side (Java) implementations because the messages are serialized and deserialized on both the client and the server. You can also create messages directly in Java and have those messages delivered to clients using the server push API.
Some message types, such as AcknowledgeMessage and CommandMessage, are used across different Flex components and BlazeDS services. Other message types are used by specific Flex components and BlazeDSservices. Forexample, to have a Producer component send a message to subscribed Consumer components, you create a message of type AsyncMessage and pass it to the send() method of the Producer component.
In other situations, you do not write code for constructing and sending messages. For example, you simply use a RemoteObject component to call the remote method from the Flex application. The RemoteObject component creates a RemotingMessage to encapsulate the RemoteObject call. In response it receives an AcknowledgeMessage from the server. The AcknowledgeMessage is encapsulated in a ResultEvent in the Flex application.
Sometimes you must create a message to send to the server. For example, you could send a message by creating an AsyncMessage and passing it to a Producer.
Services and destinations Services and destinations are the next links in the message processing chain in the BlazeDS server. The system includes four services and their corresponding destinationsBlazeDS: RemotingService and RemotingDestination HTPProxyService and HTTPProxyDestination MessageService and MessageDestination Services are the targets of messages from client-side Flex components. Think of destinations as instances of a service configured in a certain way. For example, a RemoteObject component is used on the Flex client to communicate with the RemotingService. In the RemoteObject component, you must specify a destination id property that refers to a remoting destination with certain properties, such as the class you want to invoke methods on. The mapping between client-side Flex components and BlazeDS services is as follows: HTTPService and WebService communicate with HTTPProxyService/HTTPProxyDestination RemoteObject communicates with RemotingService/RemotingDestination Producer and Consumer communicate with MessageService/MessageDestination You can configure services and their destinations in the services-config.xml file, but it is best practice to put them in separate files as follows: RemotingService configured in the remoting-config.xml file HTTPProxyService configured in the proxy-config.xml file MessageService configured in the messaging-config.xml file
The following table describes the typical setup of the configuration files. Commented versions of these files are available in the resources/config directory of the BlazeDS installation.
Filename |
Description |
services-config.xml |
The top-level BlazeDS configuration file. This file usually contains security constraint definitions,channel definitions, and logging settings that each of the services can use. It can contain service definitions inline or include them by reference. Generally, the services are defined in the remoting-config.xml, proxy-config.xml, and messaging-config.xml. |
remoting-config.xml |
The Remoting Service configuration file, which defines Remoting Service destinations for working with remote objects. |
proxy-config.xml |
The Proxy Service configuration file, which defines Proxy Service destinations for working with web services and HTTP services (REST services). |
messaging-config.xml |
The Messaging Service configuration file, which defines Messaging Service destinations for performing publish subscribe messaging. | The file-path value is relative to the location of the services-config.xml file. The following example shows service definitions included by reference: <services> <!-- REMOTING SERVICE --> <service-include file-path="remoting-config.xml"/> <!-- PROXY SERVICE --> <service-include file-path="proxy-config.xml"/> <!-- MESSAGE SERVICE --> <service-include file-path="messaging-config.xml"/> </services>
Note: When you compile an application using mxmlc, by default the compiler references the flex-config.xml configuration file, which specifies to include the libs/player directory in the library path for Flash Player. When you compile an application for AIR, use the load-config option to the mxmlc compiler to specify the air-config.xml file, which specifies to include the libs/air directory in the library path.
Specifying the services-config.xml file in a compilation When you compile your Flex application, you typically specify the services-config.xml configuration file to the compiler. This file defines the channel URLs that the client-side Flex application uses to communicate with the BlazeDS server. Then the channel URLs are compiled into the resultant SWF file. Both client-side and server-side code use the services-config.xml configuration file. If you change anything in services-config.xml, you usually have to recompile your client-side applications and restart your server-side application for the changes to take effect. In Flex Builder, the appropriate services-config.xml file is included automatically based on the BlazeDS web application that you specified in the configuration of your Flex Builder project. When you use the mxmlc compiler, use the services option to specify the location of the file. Note: You can also create channel definitions at run time in ActionScript. In that case, you might be able to omit the reference to the services-config.xml configuration file from the compiler.
The Messaging Service lets client applications send and receive messages from other clients. In this example, create a Flex application that sends and receives messages from the same BlazeDS destination. Define the messaging destination in WEB-INF/flex/messaging-config.xml, as the following example shows:
<destination id="MessagingDestination" channels="my-amf-poll"/>
Define the my-amf-poll channel in WEB-INF/flex/services-config.xml, as the following example shows:
<channel-definition id="my-amf-poll" class="mx.messaging.channels.AMFChannel"> <endpoint url="http://{server.name}:{server.port}/{context.root}/messagebroker/amfpoll" class="flex.messaging.endpoints.AMFEndpoint"/> <properties> <polling-enabled>true</polling-enabled> <polling-interval-seconds>1</polling-interval-seconds> </properties> </channel-definition>
This channel definition creates a polling channel with a polling interval of 1 second. Therefore, the client sends a poll message to the server every second to request new messages. Use a polling channel because it is the easiest way for the client to receive updates. Other options include polling with piggybacking, long-polling, and streaming.The following Flex client application uses the Producer component to send a message to the destination, and the Consumer component to receive messages sent to the destination. To send the message, the Producer first creates an instance of the AsyncMessage class and then sets its body property to the message. Then, it calls the Producer.send() method to send it. To receive messages, the Consumer first calls the Consumer.subscribe() method to subscribe to messages sent to a specific destination.
<?xml version="1.0"?> <!-- intro\intro_messaging.mxml --> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" width="100%" height="100%" creationComplete="consumer.subscribe();"> <mx:Script> <![CDATA[ import mx.messaging.events.MessageFaultEvent; import mx.messaging.events.MessageEvent; import mx.messaging.messages.AsyncMessage; import mx.messaging.Producer; import mx.messaging.Consumer; // Send the message in response to a Button click. private function sendMessage():void { var msg:AsyncMessage = new AsyncMessage(); msg.body = "Foo"; producer.send(msg); } // Handle the received message. private function messageHandler(event:MessageEvent):void { ta.text += "Consumer received message: "+ event.message.body + "\n"; } // Handle a message fault. private function faultHandler(event:MessageFaultEvent):void { ta.text += "Received fault: " + event.faultString + "\n"; } ]]> </mx:Script> <mx:Producer id="producer" destination="MessagingDestination" fault="faultHandler(event);"/> <mx:Consumer id="consumer" destination="MessagingDestination" fault="faultHandler(event);" message="messageHandler(event);"/> <mx:Button label="Send" click="sendMessage();"/> <mx:TextArea id="ta" width="100%" height="100%"/> </mx:Application>
Compile the client application into a SWF file by using Flex Builder or the mxmlc compiler, and then deploy it to your web application.
|