基于Athena framework快速创建Java Flex应用入门教程

Categories: FlexJava; Tagged with: ; @ April 23rd, 2011 22:05

本教程简要介绍基于Athena框架的Flex应用开发. 假定你已具备基本的Flex + Java开发技能. 我们将使用Athena框架快速创建一个类似与Adobe Flex Test Drive的小应用(链接), 该应用与Adobe Flex Test Drive的不同之处在于: Flex端采用了Athena Framework, 并基于Athena Framework增加了服务器端的支持.

由于时间有限, 本例仅展示环境配置,  基于Athena Console管理数据结构并自动生成Java, Flex代码,  通过简单编程实现基本功能.

所有的代码均可通过: http://code.google.com/p/athenahelloworld/ checkout(不含lib).

本文分三部分:

  1. 基于Athena框架的Flex入门教程(1) 环境配置
  2. 基于Athena框架的Flex入门教程(2)配置Entity, 自动生成Java端, Flex端代码
  3. 基于Athena框架的Flex入门教程(3) 编写Java Flex两端代码,运行程序

下图为Athena framework官方提供的Test Drive最后效果图, 可进行Employ的CRUD, 并列出Department的所有employ.

image_thumb2

基于Athena框架的Flex入门教程(3) 编写Java Flex两端代码,运行程序

Categories: FlexJava; Tagged with: ; @ April 23rd, 2011 20:04

在教程1中已经创建好了Java工程及Flex工程;
在教程2中已经使用AthenaConsole及metadata workbench配置好了数据库,并进行了建模及代码生成;

本节我们将编写Java端及Flex端代码, 并最终运行程序;

服务器端编程 – 创建被配置Service

在Athena框架下, Flex可以直接呼叫Java端的Service.  除了Service Class本身之外, 还需要在xml文件中声明该类, 以便Flex端呼叫使用.

创建Service – DeptService.java

Athena框架下的ServiceClass负责响应Flex端的请求, 与数据库进行交互. 我们可继承org.athenasource.framework.eo.web.service.AbstractService类以快速创建Service.

package com.liguoliang.helloworld.service;

import java.util.List;

import org.athenasource.framework.eo.core.EOContext;
import org.athenasource.framework.eo.query.EJBQLSelect;
import org.athenasource.framework.eo.web.service.AbstractService;

public class DeptService extends AbstractService {

	/**
	 * 加载Department列表
	 * @return
	 */
	public List<Object> loadDepts() {
		EOContext eoContext = createEOContext(); // 创建Athena EoContext;

		String strEJBQL = "SELECT dept FROM Department dept"; // 加载所有Department;
		EJBQLSelect select = eoContext.createSelectQuery(strEJBQL);

		return select.getResultList(); // 执行EJBQL, 并返回所有数据;
	}
}

 

在Service配置文件中声明Service

为了便于Flex端呼叫Service, 我们需要事先创建eo-services.xml配置文件, 该文件位于: WEB-INFO 根目录下, 该目录下有我们在教程1中介绍的eo-config.xml, flex-services.config.xml文件.

我们需要创一个专门存放Service的xml文件: eo-services.xml, 该文件的样例请参考官方文档, 我们增加刚才创建的DeptService:

<service class=”com.liguoliang.helloworld.service.DeptService;” name=”deptService” description=”AthenaFramework HelloWorld” />

 

Java端工程目录见文末附图;

至此, 我们已经创建并配置好了DeptService, 万事俱备, 只等Flex端来Call了!

Flex端编程

在Flex端工程中需要将athena-flx-2.0.0.swc放入labs目录.

将在Application创建完毕后立即初始化EoService, 基于EoService可呼叫Java端定义的Service.

			import com.liguoliang.helloworld.Department;

			import mx.collections.ArrayCollection;
			import mx.controls.Alert;
			import mx.events.FlexEvent;

			import org.athenasource.framework.eo.core.EOService;
			import org.athenasource.framework.eo.core.ioc.EOServiceLocator;
			import org.athenasource.framework.eo.remoting.event.EventEOService;
			import org.athenasource.framework.eo.remoting.event.EventRemoteOperationError;
			import org.athenasource.framework.eo.remoting.event.EventRemoteOperationSuccess;

			/** EOService */
			protected var eoService:EOService;

			/**
			 * 创建完毕后响应, 初始化EOService(将会加载Metadata基础数据)
			 */
			protected function onCreationComplete(event:FlexEvent):void {
				// Initialize eoService
				eoService = new EOService("http://localhost:8080/AthenaHelloWorld/messagebroker/amf", "eo", 2, true, onEoServiceEvent);
				// Set Service Locator
				EOServiceLocator.getInstance().eoService = eoService;
			}

			/**
			 * EoService初始化完毕, 如果成功则立即加载Department列表.
			 */
			protected function onEoServiceEvent(event:EventEOService):void {
				if(event.kind == EventEOService.KIND_LOGIN_SUCCESS) {
					trace("Metadata 加载成功");
					loadDepts(); // 加载列表
				}else if(event.kind == EventEOService.KIND_LOGIN_ERROR || event.kind == EventEOService.KIND_META_LOAD_ERROR) {
					Alert.show("ERROR: " + event.errorMessage);
				}
			}

			// 加载Department列表 - EoService已初始化完毕, 在任意位置使用EOServiceLocator.getInstance().eoService即可拿到eoService实例;
			// 使用eoService呼叫Java端创建的Service.
			private function loadDepts():void {
				// 注意首个参数serviceName - 需要与Java端eo-service.xml中配置的Service name相同.
				EOServiceLocator.getInstance().eoService.invokeService("deptService", "loadDepts", [], onLoadDeptsSuccess, onLoadDeptsError);
			}

			// 加载成功后响应.
			private function onLoadDeptsSuccess(e:EventRemoteOperationSuccess):void {
				var deptsAC:ArrayCollection = e.data as ArrayCollection;
				trace("加载到的Dept列表长度: " + deptsAC.length);
				datagirdDepts.dataProvider = deptsAC; // deptsAC中数据为Department类的实例.
			}

			private function onLoadDeptsError(e:EventRemoteOperationError):void {
				Alert.show(e.exceptionDetails, "加载失败!");
			}

 

启动Java服务器, 运行Flex应用

枯燥的配置只是为了更简单流畅的开发, 终于到了真正运行的时刻了

(在运行之前, 为了方便演示, 我已在数据库中插入了两条测试记录)

  1. 启动Java端服务器
  2. 运行Flex端应用

运行成功:

image

服务器端都做了什么? EJBQL被翻译成了什么SQL?

可通过配置Java端src根目录下log4j.properties文件以查看更多debug信息(如不存在请创建)

[http-8080-1] INFO  uery.EJBQLSelect – EJBQL: SELECT dept FROM Department dept
[http-8080-1] INFO  uery.EJBQLSelect – SQL: SELECT dept.department_ID, dept.version, dept.status, dept.ORG_ID, dept.deptName FROM Department dept WHERE dept.status <> 4

下载本例代码 & 走的更远

本例代码可通过Google code checkout: http://code.google.com/p/athenahelloworld/

其中包含: Java, Flex工程文件(均不含lib), 数据库sql文件(包含在Java工程中),

可下载Athena framework官方示例程序进一步体验: http://athenasource.org/flex/basic-tutorial.php

附录: helloworld最终目录结构:

image

基于Athena框架的Flex入门教程(2)配置Entity, 自动生成Java端, Flex端代码

Categories: FlexJava; Tagged with: ; @ April 23rd, 2011 15:50

使用Athena Console(控制台)管理数据库, 管理Entity, 生成代码

基于Athena Console可快速创建并管理数据库, 维护数据库结构, 并自动生成代码.

使用Athena Console创建并管理数据库

控制台基于前文所述的eo-config.xml文件运行, 因此启动后应选择配置文件

image_thumb[4]

由于数据库并不存在, 因此”Check database”时会提示未知数据库, 此时可点击”Create database”自动创建数据库:
“Creating database: helloWorld …
Database created successfully.”

helloWorld数据库被创建, 但没有任何table. 点击”Initialize database”, 会自动创建框架用于存储数据库结构的内置Table:

image_thumb[6]

这些Table用于存储MetaData, 如: Entity存储Table信息, attribute存储Table中字段信息, relationship存储entity关系信息(如department.employees)

此时数据库已初始化完毕, 点击控制台上方按钮进行建模:

数据建模 Entity Modeling

点击Launch metadata workbench后会自动启动浏览器, 打开基于Flex创建的Metadata管理器. 通过该管理器可快速创建Entity, 配置Entity的Attribute及RelationShip.

基于Metadata workbench可直接将程序员从数据库处理中解脱处理, 所有操作均在workbench中操作即可达成Table创建, 字段维护等操作.

我们会创建一个Department的Table(Entity), 其中包含有deptName 字段(Attribute)

创建Entity:image_thumb[15]

System name将作为Entity生成代码后的Class名称;
Table name指Table名称(该Table会自动创建, 无需手动操作)
Package name配置了代码生成时的Package路径;
Display name为默认的显示名称; i18n name可自动进行i18n国际化.

为Department增加Attribue:

image_thumb[12]

值得一提的是:

System name会作为Java及Flex两端代码的属性名称; 在命名时会禁止使用各数据库的保留字段;
Column type内置几乎所有的类型, 可根据具体数据库自动创建字段;

配置完毕后保存Entity, 返回控制台程序.

生成代码

控制台会根据eo-config.xml配置的代码生成路径, 自动生成Java ActionScript代码:

image_thumb[17]

代码生成完毕, 刷新Java与Flex工程, 可以看到代码已生成:

image_thumb[19]

image_thumb[21]

每个Entity会为每种语言自动生成两个Class, 如: Department_EO, Department;

Department_EO:  当metadata有变化, 再次生成代码时, 将会覆盖本类, 因此不建议对本类进行任何手工改动;
Department: 该类继承自: Department_EO仅当第一次生成代码时产生该类, 以后再次生成将不会进行任何覆盖操作; 因此可对重写父类Method或增加其他Method;

基于Athena框架的Flex入门教程(1) 环境配置

Categories: FlexJava; Tagged with: ; @ April 23rd, 2011 15:44

环境搭建

在服务器端, 我们采用MySql数据库;

除了开发必须的IDE, MySql之外, 需要先下载或安装以下文件: 下载页面

  • Athena Framework SDK (Java and Flex) 2.0.0 – 包含Java端及Flex端的SDK及文档
  • Athena Console 2.0.0 – 控制台程序, 用于配置Java端数据库及管理Metadata.

image

docs: 存放文档, 与Flex文档类似, 有HTML及PDF;
lib: 存放程序运行依赖的jar, 如mysql-connector, BlazeDS等jar包;
src: 目录存放程序源代码

athena打头的jar供Java端使用;
athena打头的swc供Flex端使用; sdk的具体使用在后文有介绍;

 

服务器端配置

创建Java Project

  1. File -> New -> Dynamic Web Project
  2. 复制所有jar到PROJECT_ROOT/WebContent/WEB-INF/lib
  3. 配置web.xml, 用以启动Athena及BlazeDS服务;
  4. 创建两个配置文件: eo-config.xml 与 flex-services.config.xml;
    eo-config.xml中配置了数据库类型, 数据库连接信息, 以及代码存放位置(供Athena控制台工具使用), 该xml中java-source-local-dir属性为Java src的根目录, flex-source-local-dir为Flex src的根目录(待Flex工程创建完毕后配置);
    flex-services.config.xml为MessageBrokerServlet(BlazeDS)的配置文件;

配置可参见http://athenasource.org/flex/basic-tutorial.php?part=1

至此, 服务器端已基本配置完毕;

Flex端配置

使用FlashBuilder创建工程:

注意: server type选择None/Other, 我们使用编程方式链接服务器;

复制src目录地址, 配置到flex-services.config.xml中, 为下一步的代码生成做好准备;

BlazeDS Flex端常用方法总结 及 ResultEvent分析

Categories: Flex; Tagged with: ; @ February 21st, 2009 20:24

1 .基本使用:

	private var op:AbstractOperation;
	public function loadEmployees():void {
		op = AppContext.getRemoteObject().getOperation("loadEmployees");	//获得Operation
		 op.arguments = [id];	//设定参数
		 var at:AsyncToken = op.send();	//Send
		 at.addResponder(this);	//为本实例增加responder

      }

//---------------------实现 IResponder-------------------------------
	public function result(responderResult:Object):void {
		var resultEvent:ResultEvent = responderResult as ResultEvent;
		var ac:ArrayCollection = resultEvent.result as ArrayCollection;//result为服务器返回的数据
		log.debug("已读取到 " + this + "的职员列表, 职员数目为: " + ac.length);
	}

	public function fault(data:Object):void {
		throw new Error("远程操作失败");
	}

2. 另外可通过外置Responder实例来响应结果, 同时在实例中dispatch Event, 可实现在当前类中响应服务器端返回数据.

	//Listener - onButtonDelClicked
	private function onButtonDelClicked(e:MouseEvent):void {
		var op:AbstractOperation = AppContext.getRemoteObject().getOperation("delEmp");
		log.info("Deleteing...");
			 op.arguments = [datagridEmpList.selectedItem as Employee];
			 var at:AsyncToken = op.send();
			 var rs:OperationResponder = new OperationResponder();//增加Responder实例
			 rs.addEventListener(OperationResultEvent.OPERATION_COMPLETE, onOperationSuccess);//监听由Responder派发的Custom Event
			 at.addResponder(rs);//为AsyncToken增加Responder
	}

//-----------------------on RemoteOperationSuccess - SaveNew - UPdate - Delete - load Department- 监听多个操作的Event-----------------
	private function onOperationSuccess(e:OperationResultEvent):void {
		var resultEvent:ResultEvent = e.resultData as ResultEvent;
		var resultEventATMessage:RemotingMessage = resultEvent.token.message as RemotingMessage;
		var operationName:String = resultEventATMessage.operation;
		if(operationName == "saveEmpToDB") {//新建保存
			//....
		}else if(operationName == "updateEmp"){	//更新
			//....
			}
		}else if(operationName == "delEmp") {//删除
			var deletedEmp:Employee = resultEventATMessage.body[0];
			log.info("已成功删除职员: " + deletedEmp.name);
		}else if(operationName == "loadDepartments") {	//加载部门信息
		//...
		}
	}

OperationResponder类:

package com.insprise.dept.view
{
/**
 * Select Departments Responder
 */
 [Event(name = "OperationComplete", type="com.insprise.dept.event.OperationResultEvent")]
public class OperationResponder  extends EventDispatcher implements IResponder
{
	public function OperationResponder() {
		super();
	}

	public function result(data:Object):void {
//		var resultEvent:ResultEvent = data as ResultEvent;
//		var ac:ArrayCollection = resultEvent.result as ArrayCollection;
		dispatchEvent(new OperationResultEvent(data));
	}

	public function fault(info:Object):void {
		throw new Error("服务器出错 - 调用服务器端方法失败" +  ErrorMessage);
	}
}//end of class
}//end of package

OperationResultEvent类:

package com.insprise.dept.event
{
/**
 * when Operation Success, and it's responder can dispatch this event
 */
public class OperationResultEvent extends Event
{
	public static const OPERATION_COMPLETE:String = "OperationComplete";
	private var _resultData:Object;

	/**
	 * Constructor
	 */
	public function OperationResultEvent(resultData_:Object) {
		super(OPERATION_COMPLETE, false, false);
		_resultData = resultData_;
	}

	/**
	 * Getter
	 */
	 public function get resultData():Object {
	 	return _resultData;
	 }

	/**
	 * Override - MUST
	 */
	 override public function clone():Event {
	 	return new OperationResultEvent(_resultData);
	 }

	/**
	 * OPTIONAL
	 */
	 override public function toString():String {
	 	return formatToString(OPERATION_COMPLETE, "_resultData");
	 }
}//end of class
}//end of package

3: ResultEvent常用操作:

ResultEvent由AsyncToken的applyResult方法派发, 将该Event传递至Responder的result方法中.

ResultEvent的属性:

    /**
     * Creates a new ResultEvent.
     * @param type The event type; indicates the action that triggered the event.
     * @param bubbles Specifies whether the event can bubble up the display list hierarchy.
     * @param cancelable Specifies whether the behavior associated with the event can be prevented.
     * @param result Object that holds the actual result of the call.
     * @param token Token that represents the call to the method. Used in the asynchronous completion token pattern.
     * @param message Source Message of the result.
     */
    public function ResultEvent(type:String, bubbles:Boolean = false, cancelable:Boolean = true,
                                result:Object = null, token:AsyncToken = null, message:IMessage = null)
    {
        super(type, bubbles, cancelable, token, message);

        if (message != null && message.headers != null)
            _statusCode = message.headers[AbstractMessage.STATUS_CODE_HEADER] as int;

        _result = result;
    }

image

resultEvent.result存放有当前op返回值, 可通过ObjectUtils.toString(resultEvent.result)来查看其类型及信息

其中token中存放有如下具体信息:

image

Message中的内容:

body中存放有当前Operation的很多参数, 可使用如下方法调用Message:

var resultEventATMessage:RemotingMessage = resultEvent.token.message as RemotingMessage;

然后可通过resultEventATMessage.operation / desination/body[*]等来调用op/destination名字及op的参数.

在Operation中定义了message的内容:

以下代码来自mx.rpc.remoting.Operaiton

    override public function send(... args:Array):AsyncToken
    {
        if (!args || (args.length == 0 && this.arguments))
        {
            if (this.arguments is Array)
            {
                args = this.arguments as Array;
            }
            else
            {
                args = [];
                for (var i:int = 0; i < argumentNames.length; ++i)
                {
                    args[i] = this.arguments[argumentNames[i]];
                }
            }
        }

        var message:RemotingMessage = new RemotingMessage();
        message.operation = name;
        message.body = args;
        message.source = RemoteObject(service).source;

        return invoke(message);
    }

}

Older Posts



// Proudly powered by Apache, PHP, MySQL, WordPress, Bootstrap, etc,.