扩展EGL支持Google App Engine Datastore数据存储

时间:2022-07-20 16:39:24

1 简介

建议:读者在开始做对Google App Engine的扩展之前,需要了解和具备以下的基础知识:
  • EGL以及EDT的知识
  • Google App Engine的数据存储(Data Store)部分
  • Eclipse插件开发的知识和经验
  • Java EE的基础知识


    EGL作为一门业务语言,提供了一套抽象的面向业务开发的语言模型。EGL程序员能利用这套面向业务的语言模型方便、快捷地开发出跨平台的、跨语言的各种业务系统。EGL针对业务系统里经常用到的数据访问操作定义了一系列语句,比如,

  • get:从某数据源里取得数据
  • add:往某数据源插入数据
  • replace:向某数据源更新数据
  • delete:从某数据源删除数据
     这些EGL提供的抽象的数据访问的操作语句,可以进行扩展。如下图所示,这些抽象的数据存取操作可以是EGL现在已经支持的基于Java JDBC关系数据库的实现,也可以是基于Java的文件系统的实现,或者是.net C#的ODBC实现等,甚至是Google App Engine提供的Datastore数据存取实现。

扩展EGL支持Google App Engine Datastore数据存储

GoogleApp Engine提供了一种不同于关系型数据库的数据存储,基于BigTable的Datastore。通过把EGL数据操作语句扩展到Datastore,EGL使用者可以在不了解Datastore技术细节的情况下轻易写出能够利用Datastore存储的应用程序。甚至可以几乎不做改动的将一个使用SQL存储的程序切换成使用Datastore存储。由此意味着EGL程序员在不改变应用程序的前提下,通过扩展EGL,能毫不费劲地把一个基于Java JDBC关系数据库实现的应用无缝的迁移到任何新出现的平台上(包括我们即将要实现的扩展GAE BigTable DataStore),这样能大大提高应用程序的开发效率和降低开发成本。

    这篇blog介绍如何扩展EGL中的add语句来调用GAE的存储API,带领大家一起来学习EGL是如何在语言模型、代码生成器上进行扩展的。


2. 开发目标

     本文的目标是扩展EGL,用于实现和Google App Engine数据存储的集成。我们展示了如何用EGL语言来实现向GoogleApp Engine数据存储中插入实体数据的过程,并给出一个包括EGL Rich UI和EGL REST Service的样例,最终将该样例部署到Google App Engine上。

3. 扩展过程概述

    这里先大概介绍一下进行EGL扩展的策略。

    第一,需要先掌握目标扩展平台的相关知识;

    第二,参考现有的EGL语言模型,看是否能够适应要扩展的目标平台;

    第三,手工先实现一下将要被生成的目标平台的代码,为实现自己的代码生成器做准备;

    第四,总结目标生成代码,决定是否有必要提炼出一个目标平台的运行时,以方便代码生成。

3.1和3.2简单概述了扩展EGL的步骤,具体的细节,请参考章节4、5和6。

3.1 模型扩展

    通常,针对数据模型的存取,EGL会提供一个数据源(DataSource)作为数据存取和访问的入口点。如针对关系数据库,用户可以使用SQLDataSource来进行数据的增、删、改、查。

    同样,如果需要向GoogleApp Engine的数据存储(Data Store)进行数据的存取和访问等操作,也需要一个数据源,这就需要我们对已有的EGL模型进行扩展。以下是扩展的一个简单流程:

  • 创建Google App Engine的数据存储的数据源GAEDataSource。
  • 定义和Google App Engine的数据存储相关的EGL模型。
  •  向EGL编译器注册自定义的EGL模型。

3.2 代码生成器扩展

   在扩展完GAEDataSource和相应的EGL模型后,还需要对代码生成器(EGL Generator)进行扩展,从而生成相应的和Google App Engine相集成的Java代码。

   基于Java的代码生成器的扩展,一般有两种方式:

  • 创建新的代码生成器。一般是创建新的代码生成器如GAEGenerator、相应的模板以及模板的描述文件templates.properties,并且需要对界面作一些扩展。
  • 扩展已有的代码生成器。这种情况相对简单,一般只需创建新的模板以及模板描述文件templates.properties即可。

   在本文中,我们采用第二种方式。关于如何扩展代码生成器,读者可以参考DW中国上的EGL系列文章的第四篇来了解更多详细信息【待定】。

4. 设置开发环境

4.1 装GAE插件

    关于如何安装和配置GAE插件,读者可以访问Google App Engine的官方文档https://developers.google.com/appengine/downloads#Download_the_Google_Plugin_for_Eclipse去自行安装和设置。

4.2 安装设置EDT开发环境

    关于如何安装EDT以及配置EDT等具体的技术细节,读者可以参考在线帮助文档“EGL语言从入门到精通:http://blog.csdn.net/rationalgroup/article/details/7336753”。

4.3 设置开发工作空间

    在安装和配置完GAE和EDT插件后,读者需要从Eclipse的CVS上检出EDT相关的源代码。具体步骤如下:

  • 切换到CVS Repository Exploring透视图下
  • 增加一个CVS Repository: pserver:anonymous@dev.eclipse.org:/cvsroot/tools
  • 在CVS Repositories视图中,导航到EDT的目录下,如图所示:
扩展EGL支持Google App Engine Datastore数据存储

检出所需要的插件项目。在这里读者需要注意的是应该检出每个项目的EDT080GM版本,如图所示:

扩展EGL支持Google App Engine Datastore数据存储


扩展EGL支持Google App Engine Datastore数据存储


扩展EGL支持Google App Engine Datastore数据存储

  • 用以上方法检出所需要的以下插件项目(当然读者也可以将org.eclipse.edt下所有插件项目都检出到工作空间中):
    • org.eclipse.edt.compiler
    • org.eclipse.edt.ide.compiler
    • org.eclipse.edt.mof.egl
    • org.eclipse.edt.runtime.java

5. 模型扩展

5.1 创建GAEDataSource外部类型

Google App Engine的数据储存代表一个新的数据源,因此在这里我们需要创建一个新的EGL版型GAEDataSource来表示它。

在插件org.eclipse.edt.compiler中新建一个EGL包eglx.gae,并创建一个外部类型(ExternalType)。这个外部类型用于定义一个DataSource,用于和其他DataSource进行区分。

externalType GAEDataSource extends DataSource type NativeType  
end

扩展EGL支持Google App Engine Datastore数据存储

GAEDataSource的程序目录结构

创建完外部类型以后,需要运行tools/build-CompilerEGLAR.xml的ANT脚本来在lib目录下编译生成相应的EGLAR文件edtCompiler.eglar。

接下来需要在插件org.eclipse.edt.runtime.java中添加一个空实现的Java类eglx.gae.GAEDataSource.java。具体的程序结构读者可以自行参考附件中的样例程序【

http://download.csdn.net/detail/rationalgroup/4326397   】。

5.2 扩展EGL Model

在创建完GAEDataSource以后,需要扩展相应的EGL Model来标识和Google AppEngine数据储存相关的模型。在本文中,我们仅给出add语句的样例,读者可以参考这个实现来实现其他的抽象语句(如get, replace, delete)。

首先在EDT中创建一个插件项目org.eclipse.edt.mof.eglx.gae,右击该项目,在右键菜单中选择Configure -> Convert to EGL Project,这样该项目就转换成一个EGL插件项目。

在该项目中创建一个EGL源文件GAEActionStatement.egl:


externalType GAEActionStatement extends IOStatement type MofClass  
end

externalType GAEAddStatement extends GAEActionStatement, AddStatement type MofClass
end
同时需要在该插件中创建与以上所定义的EGL Model相对应的Java类(参考样例程序【
http://download.csdn.net/detail/rationalgroup/4326397 
】),这个插件的目录结构如图所示:

扩展EGL支持Google App Engine Datastore数据存储

org.eclipse.edt.mof.eglx.gae插件

运行tools/build-MofEglMOFAR.xml生成egllib下面的mofar和eglar包:gae.eglar和gae.mofar(读者可以拷贝样例程序中的Ant脚本到该项目中)。

这个插件的作用类似于源代码中的org.eclipse.edt.mof.eglx.services插件,具体的Java接口和实现读者可以参考这个插件。

5.3 注册扩展EGL模型

    创建完EGL Model以后,我们需要在EGL的Compiler插件org.eclipse.edt.compiler注册这些模型,使得Compiler可以识别这些新定义的EGL Model。

  • 在插件org.eclipse.edt.compiler添加类org.eclipse.edt.compiler.internal.egl2mof.eglx.gae.GAEActionStatementGenerator。这个类作用类似于org.eclipse.edt.compiler.internal.egl2mof.eglx.services.ServicesActionStatementGenerator用来把新定义的外部类型和EGL compiler链接在一起。具体的源码读者可以参考附件中的样例程序
    http://download.csdn.net/detail/rationalgroup/4326397  。
  • 把类GAEActionStatement注册到类org.eclipse.edt.compiler.EDTCompiler。加入
  •  path +=SystemEnvironmentUtil.getSystemLibraryPath(GAEActionStatement.class,"egllib");
  • 把插件org.eclipse.edt.mof.eglx.gae 注册到类org.eclipse.edt.ide.compiler.IDEEDTCompiler。加入
  •  path +=getPathToPluginDirectory("org.eclipse.edt.mof.eglx.gae","egllib");
  • 把类GAEActionStatementGenerator注册到类org.eclipse.edt.compiler.internal.egl2mof.Egl2MofStatement。加入
  • IOStatementGenerator.Registry.put("eglx.gae",GAEActionStatementGenerator.class);

6. 代码生成器扩展

6.1 扩展代码生成器

    在完成EGL Model的定义和相应的Compiler注册以后,我们需要为这些新定义的EGL Model来定制和扩展相应的EGL Generator,用来产生和Google App Engine集成的Java代码。

    新建一个GAE模板项目org.eclipse.edt.gen.java.templates.eglx.gae(读者可以参考SQL相关的模板项目org.eclipse.edt.gen.java.templates.eglx.persistence),并创建相应的模板GAEAddStatementTemplate。源代码如下:

public class GAEAddStatementTemplate extends org.eclipse.edt.gen.java.templates.StatementTemplate {  

public void genStatementBody(GAEAddStatement stmt, Context ctx, TabbedWriter out) {

Expression expression = stmt.getTarget();
Type type = expression.getType();

EGLClass targetType = (EGLClass) type.getClassifier();

String recordName = targetType.getName();

out.println("com.google.appengine.api.datastore.DatastoreService datastore = com.google.appengine.api.datastore.DatastoreServiceFactory.getDatastoreService();");
out.print("com.google.appengine.api.datastore.Entity ");
String googleEntity = "google_" + recordName;
out.print(googleEntity);
out.print(" = new com.google.appengine.api.datastore.Entity( " + "\"" + recordName + "\"" + " )");
out.print(";");
out.println();

for (Field f : targetType.getFields()) {

out.print(googleEntity + "." + "setProperty(\"");
ctx.invoke(genName, f, ctx, out);

TabbedWriter temp = ctx.getTabbedWriter();
ctx.invoke(genExpression, expression, ctx, temp);
String varName = temp.getCurrentLine();
out.print("\"," + varName + ".");
ctx.invoke(genName, f, ctx, out);
out.print(");");

out.println();
}

out.print("datastore.put(" + googleEntity + ");");

}
}
同时,需要创建一个模板文件用于把新定义的EGL Model和相应的Generator绑定在一起,源文件如下:

org.eclipse.edt.mof.eglx.gae.GAEAddStatement = org.eclipse.edt.gen.java.templates.eglx.gae.GAEAddStatementTemplate  
到此为止,和GoogleApp Engine数据存储集成相关的EGL扩展就完成了,接下来我们将给出一个样例来展示如何使用扩展后的EGL来实现和Google App Engine数据存储集成,并且部署到Google App Engine上。

7. 样例

7.1 开发样例程序

创建一个EGL RichUI项目“RuiTesting”,选择“Web2.0 client application withservices”作为模板。

在RuiTesting项目里,创建一个EGL Record,源代码如下:

record OrderItem  
itemId int{@Id};//{@Id, @GeneratedValue };
//NAME string{@GeneratedValue};
name string{ @Column { insertable=true } };
end
创建一个EGL REST Service,源代码如下:
service HelloService  
function sayHello(name string in) returns(string)
itemEntity OrderItem;
//ds SQLDataSource? = new SQLDataSource("jdbc:derby:C:/databases/EGLDerbyR7;create=true");
ds GAEDataSource? = new GAEDataSource();

add itemEntity to ds;
return("Hello World:" + name);
end
end
最后,创建一个RUI Handler来调用Service。在按钮的事件中来调用Service,源码如下。完整的程序,读者可以参考样例的下载。
function serviceButton_onClick(event Event in)  
call helloService.sayHello(nameField.text) returning to handleReturn onException Exceptionhandler;
end

function handleReturn(results string in)
SysLib.writeStdout("Got service result:" + results);
end

private function Exceptionhandler( exception AnyException in)
handleException(exception, null);
end

private function handleException( exception AnyException in, srvc string? in)
SysLib.writeStdout(exception.message);
end
EGL Service产生的Java代码如下,这里调用了Google AppEngine数据存储的API。
public String sayHello(String name) {  
OrderItem itemEntity = new OrderItem();
GAEDataSource ds;
ds = new GAEDataSource();
com.google.appengine.api.datastore.DatastoreService datastore = com.google.appengine.api.datastore.DatastoreServiceFactory.getDatastoreService();
com.google.appengine.api.datastore.Entity google_OrderItem = new com.google.appengine.api.datastore.Entity( "OrderItem" );
google_OrderItem.setProperty("itemId",itemEntity.itemId);
google_OrderItem.setProperty("name",itemEntity.name);
datastore.put(google_OrderItem);;
return (("Hello World:") + name);
}

7.2 部署并在本地运行

Google App Engine的Eclipse插件提供了本地的运行环境。因此,我们在这里介绍一下如何将7.1中开发的样例程序进行部署并在本地运行。

使用EGL Deployment Descriptor Editor来打开RuiTesting/EGLSource/RuiTesting.egldd文件,在Resource Binding标签中,设置URI为http://localhost:8888/restservices/HelloService

需要注意的是,由于GoogleApp Engine的Eclipse插件所提供的Web Application项目结构并非一个标准的Eclipse下的动态Web项目,为了EGL程序的部署需要,我们需要对GAE的项目结构做一些调整,使得它转换成一个标准的Eclipse动态Web项目。

首先创建一个Google App Engine的项目RuiTestingDeploy,在项目属性的Project Facet中设置DynamicWeb Module的版本为2.4。并设置WAR的目录为WebContent,如图所示。
扩展EGL支持Google App Engine Datastore数据存储设置WAR的目录

其次在原有的war目录下,将Google App Engine的部署描述符appengine-web.xml和Log4J的配置文件logging.properties拷贝到WebContent目录下。并删除原有的war目录。

    开始部署样例程序到RuiTestingDeploy中。部署完毕以后,修改下列文件:

  • src/RuiTestingDeploy-uri.xml。将contextroot元素的值设置为/restservices(如果开发的是Dedicated Service,则忽略这一步)。
  • WebContent/RuiTesting.html。将egl.initParams()函数的第一个参数设置为”.”。

   在GAE环境中,运行RuiTesting.html,关于怎么在本地运行GAE程序,读者可以参考GAE的官方文档。

8. 下一步

本篇博客只是对EGL中的add语句在GAE平台上的扩展做了初步探讨,接下来需要读者实现对EGL里的其它抽象操作语言(replace, delete和get)的扩展实现,并使用扩展后的基于GAE的EGL实现一个针对数据库访问的应用(前台使用EGLRichUI,后台使用基于GAE的EGL服务),并且需要部署在Google App Engine上。