UML软件工程组织

 

 

围绕EMF探索
 
作者:胡长城(银狐999)BLOG 出处:CSDN
 
围绕EMF探索(1)之存储

    EMF(Eclipse Modeling Framework)的应用会越来越多,而围绕EMF的query, validate, transaction, persistency等等讲逐渐被大家使用,以及EMF eCore也有非常大的参考价值。今天先讲讲EMF的存储与查询方面的内容。

首先讲讲EMF对象的存储

    这两天抽空玩了玩EMF的对象存储,采用了Eclipse Teneo这个组件。这是一种桥梁式的组件,为EMF对象提供了一个持久化的解决方案。目前支持Hibernate和JDO的实现方式。Eclipse Teneo的前身是elver persistency组件,后并入Eclipse EMF Tools项目工程下。

 
    Teneo让EMF对象的存储变得非常简单。这样我们只需要关注EMF对象的关系,而不用过多的考虑每一个eObject该如何与数据库表对应。
    在使用Hibernate的情况下,Teneo会根据ePackage所描述的Model中的eObject对象之间的关系,自动的产生hiernate.hbm.xml映射文件。当然我们可以手工指定mapping文件来强制描述eObject与数据库表结构的关系。详细可以参看:
    http://www.elver.org/hibernate/hibernate_details.html#orgeneration
 
    简单的采用Teneo操作eObjects是非常容易的事情,在elver上也提供了那个经典的“Library Model”实例的实现例子,可参考:
    http://www.elver.org/hibernate/tutorialone/tutorial1_intro.html
 
 
final HbDataStore dataStore = HbHelper.INSTANCE.createRegisterDataStore(dataStoreName);
dataStore.setEPackages(
new EPackage[] { LibraryPackage.eINSTANCE });

final SessionFactory sessionFactory  = dataStore.getSessionFactory();
Session session = sessionFactory.openSession();
session.beginTransaction();

LibraryAdapterFactory adapterFactory = 
new LibraryAdapterFactory();

Library library  = LibraryFactory.eINSTANCE.createLibrary();
library.eAdapters().add( adapterFactory.createAdapter(library) );
library.setName("My Library");

session.save(library);

Writer writer = LibraryFactory.eINSTANCE.createWriter();
writer.setName("Writer One");

Book book = LibraryFactory.eINSTANCE.createBook();
book.setPages(305);
book.setTitle("The Hobbit");
book.setCategory(BookCategory.SCIENCE_FICTION_LITERAL);
book.getWriter().add(writer);

library.getWriters().add(writer);
library.getBooks().add(book);

session.getTransaction().commit();
session.close();
 
 
再说说EMF的查询吧。
      
    Teneo本身提供了一对查询的支持,比如针对Hibernate的实现,支持对HQL的检索。
 
Query query = session.createQuery("SELECT book FROM Book book, Writer writ WHERE "
    + " book.title='1984' AND book.author=writ AND writ.name='G. Orwell'");
books = query.list();

 
    但是,Teneo仅仅支持Hibernate的HQL,或JDO的JDOQL,略显得不够完善,或者说,让对象检索变得有些“污染”。
    我比较喜Eclipse EMF项目工程下有EMF Query组件,这个组件为“内存中的eObjects”的检索提供一套“查询条件”,但目前EMF Query仅支持内存中的对象关系,这就局限来其现实中的应用价值。
    当然,EMF Query和Teneo还是在某些不要求效率的情况下能够配合起来的。
 
public Collection queryLargeBook(EObject root) {
    SELECT select = 
new SELECT(new FROM(root), new WHERE(
            
new EObjectAttributeValueCondition(LibraryPackage.eINSTANCE
                    .getBook_Pages(), 
new NumberCondition.IntegerValue(
                    
new Integer(10), new Integer(Integer.MAX_VALUE)))));

    
return select.execute();
}
Query query = session.createQuery("FROM Library");            
List libraries = query.list();
Library library = (Library) libraries.get(0);    
Collection books = queryLargeBook(library);

 
    在内部实现上,EMF Query会把所有的源对象都装载进内存中,在内存中遍历,并依次判断是否符合所设定的查询条件。下表中代码截取自emf query的Select对象,演示了遍历寻找符合条件的对象的算法。
 
protected void doResume() {
    EObject eObject = 
null;
    setResultSet(
new QueryResultSet());
    
boolean canPrune = getFromClause().canBePruned();
    WHERE whereClause = getWhereClause();
    
while (it.hasNext() && (isCancelled() == false)) {
        eObject = (EObject) it.next();
        
if (whereClause.matches(eObject)) {
            addEObject(eObject);
            
if (getResultSet().size() == maximumResultSize) {
                
break;
            }

        }

    }

}

 
    EMF Query构造了条件(Condition)对象,这个Condition有点“(函数式编程)Function Programming”的影子了,确切的说,应该是“一元谓词(UnaryPredicate)”。有关java的FP,可参考我的另一篇blog:http://blog.csdn.net/james999/archive/2005/01/09/246404.aspx
    不过,EMF Query的Condition并没有基于任何开源FP的基础,既没有基于apache的Common Functor,也没有基于JGA(http://jga.sourceforge.net/)。
 
    EMF Query内部结构和实现,我目前还没有看完,姑且先不细说,留待后面补充。
 
围绕EMF探索(2)之再探查询组件
 
    本篇专门讲讲EMF Query这个组件的设计原理、结构和算法。
    在上一篇中,已经简单介绍了EMF Query,其是为“内存中的eObjects”的检索提供一套“查询条件”,可以依据eObject的结构关系进行查询,也可以依据eObject的对象进行查询。
 
    EMQ Query的主结构关系如下图所示:
    其中咖啡色表示最基本的设计思想的基类,把握住了这几个类(接口),则就把握了整个EMF Query的设计原理。
    其中蓝色的图形表示主要所需要关注的实现类和构架。
 
    从大的方面说,整个EMF Query是由围绕查询语句的构造和执行来展开的。
    (1)查询语句的结构
             
    查询语句的结构一般如下所示:
SELECT
       FROM [source]
       WHERE [condition]
    整个查询语句(SELECT)是由两个查询子句(Query Clause)组成,分别是FROM和WHERE。其中FROM表示了所需要查询的对象源(EObject Source),WHERE则表示了所需要匹配的条件。
    对于EObject Source,其表示的就是一组eObject对象的集合。
 
    (2)查询语句的执行
    整个查询语句的执行规则就是,将所需要检索的对象源(EObject Source)转换成一棵对象树,通过遍历这棵对象树,依次将树中的对象与所需要检索的条件进行匹配,选择匹配成功的。
 
EObject eObject = null;
boolean canPrune = getFromClause().canBePruned();
WHERE whereClause = getWhereClause();
while (it.hasNext() && (isCancelled() == false)) {
       eObject = (EObject) it.next();
       if (whereClause.matches(eObject)) {
              addEObject(eObject);
              if (getResultSet().size() == maximumResultSize) {
                     break;
              }
       }
       if (canPrune && whereClause.shouldPrune(eObject)) {
              it.prune();
       }
 
}
 
    有关上面那张类图的结构就不再多叙述,相信一看就明白。
    接下来讲讲两个比较值得关注的地方,一个是看得见的条件(Condition),还有一个是看不见的Tree Prune。
 
条件(Condition)
    与条件相关的类几乎占据了整个Query组件的一半,但是这些Condition是非常简单的。如果想真正理解这个条件的构造机制和原理,那么建议大家花点时间了解一下Java Funtion Programming(Java的函数式编程)。整个Condition就是围绕一个“一元谓词”而构造的,而每个Condition实际上就代表一个算子(arithmetic operator)。
    其实上一篇已经提到了我曾经写过一篇有关Java函数式编程的blog,参见http://blog.csdn.net/james999/archive/2005/01/09/246404.aspx 。
 
    大家在使用或阅读Query源码的时候,可能会涉及到有的Condition会存在Adapter的。其实这就是利用了一个简单的Adapter模式代码,将需要匹配的EObject转换成当前Condition可以识别的值,用于计算。
 
对象树的裁减
    前面讲过在执行检索过程中,要检索的对象源(EObject Source)转换成一棵对象树。其实是获取一个新的EMF所提供的TreeIterator对象,这是一个允许被裁减(Prune)的针对树的遍历指针。
    TreeIterator的默认遍历方式是采用“树的深度遍历算法”,而裁减(Prune)则表示:当遍历到某个节点时,如果执行裁减行为,则此节点后的所有子节点将不再遍历——就像一棵树,被削了一个枝杈似的。
    因为EMF的对象是支持包含关系的,而且很容易通过getContents()接口获取相关的内容对象。虽然如果不进行适当的裁减操作,完整的遍历一棵树,是比较缓慢的。
    EMF Query在构造Condition的时候,提供了PruneHandler接口,用于判断当前的eObject对象是否需要进行裁减遍历行为。
围绕EMF探索(3)之初探OCL

    围绕EMF模型,有个很重要的基础性组件叫EMF OCL,这是一个实现了OCL2.0标准的组件,提供了对OCL Expression的解析、校验。OCL全称是Object Constraint Language。
 
    对OCL之前接触不是很多。两年前,在MDAChina还存在的时候,见到一些朋友谈论它。但对当时的技术和应用来说,OCL还是过于“理想化”。事实上,直到今天,OCL在国内的应用也是很稀少的。
    如果不是因为探索EMF OCL这个组件的原因,我想我也很难去主动钻研OCL的那有些晦涩的Expression。虽然在UML2中,其已经成为一个用于准确描述模型的关键特性。
 
    其实,使用EMF OCL非常简单,使用QueryFactory构造一个Query对象,设置表达式(Express),以及表达式所依赖的Context(在EMF OCL中,一般是Classifier)。然后对给定eObject对象进行检查和计算。——就这么简单。
 
Query query = QueryFactory.eINSTANCE.createQuery(
    "Book.allInstances()->select(b : Book | b <> self and b.title = self.title)",
    LibraryPackage.eINSTANCE.getBook());
query.setExtentMap(extents);
Collection result = query.evaluate(myBook);
 
    当然,想很熟练的应用EMF OCL,那么就必须会构造正确的Expression。
    所以,不得不花费点时间学习OCL。
 
    OCL的概念和作用就不用在这里啰嗦了,以前的MDAChina那帮爱好者们写了很多有关这方面的文章(可惜mdachina已经不存在),比较典型的就是“MDA之路”那个兄弟写的几篇blog 文章,
http://www.blogjava.net/wxb_nudt/archive/2006/11/29/2188.html 和http://www.blogjava.net/wxb_nudt/archive/2005/04/04/2858.html 。
 
    对于更精细的了解OCL,可以读读OCL之父Addison Wesley的著作《Object Constraint Language, The: Getting Your Models Ready for MDA》第二版,可以从下面的地址获取电子书:
http://www.netyi.net/training/4d9e427c-3d58-4f74-b540-ad642c9afccc
    另外,建议大家再看看《UML2.0参考手册》附录B部分的,这部分内容也是讲OCL的,而且讲了OCL的Basic Type和Syntax。
 
    粗略的把这两本书中一些相关的章节翻了翻,还是很有收获的。当然限于时间问题,只是翻翻而已,留个大概映像,以后慢慢再研究查阅了。
 
    针对OCL,这里简单提几个重点性内容:
 
     OCL是一种语言,而且是一个Query-Only语言,利用OCL所塑造的Expression为模型中的类与类、类方法、类属性做了一层约束。既然是一种语言,那么就具有最基本的两个特性:类型和语法。
    OCL具有最基本的built-in类型,同时,会将Meta-Model中的所有自定义classifier都认为是类型。针对built-in类型,会有相应的很多逻辑操作相关联,比如“and, or, xor, not”或者“*, +, -, /”等等。这方面可以参考,里面有专门的一章节介绍OCL的类型和逻辑操作。
    针对Classifier之类的类型,则存在三个纬度:作用于Classifier的约束;作用于操作(方法)的约束;作用于属性的约束。这三个层面的约束,有不同的语法表达。
Contraints on Classifiers
context Student
inv: self.GPA > 2.0
Contraints on Operators
context Course::registerStudent(s : Student) : boolean
pre: s.tuitionPaid = true
Contraints on Attributes
context School::tuition : float
init: 2500.00
             
    另外,提醒一下。从Eclipse CVS中抓下的ocl plugin project,需要依赖eclipse orbit包中的lpgjavaruntime组件,可以去http://download.eclipse.org/tools/orbit/downloads/下载,其实就是net.sourceforge.lpg.lpgjavaruntime 包。
 
    先讲讲OCL的小知识吧,今天只顾钻研OCL的基础知识和语法了,还没有来得及研究EMF OCL的源码和结构。简单的扫了几眼,着实比EMF Query复杂很多,看来要花费更多的时间在上面了。

围绕EMF探索(4)之Validation组件图

    EMF Validation Framework提供了对EMF eObjects的校验框架。这种校验框架支持两个层面的:第一个层面可以对一个集合内的eObjects进行校验,第二个层面可以时时地对对象内的属性(值)变更进行校验。

    这几天利用有限的空余时间在钻研EMF Validation Framework。原以为Validation会比较简单(使用Validation倒是比较简单),其实却不然。为了弄清楚Validation内部实现类及构架机制,着实耗费了不少时间。
    EMF的Validation Framework使用是很比较简单的,最简单的方式可以通过扩展org.eclipse.emf.validation.constraintProviders扩展点,通过构造特定的AbstractModelConstraint实现类来对eObjects进行校验。但EMF Validation Framework的结构是非常复杂的,这也是由于Validation试图提供更加复杂全面的检验框架:
(1)       提供对Batch Validator和Live Validator的支持
(2)       提供对多种声明Constraint的方式支持,目前支持Java类的方式,就是前面提到的通过实现用户自己的AbstractModelConstraint类;支持EMF Constraint和灵活的OCL Constraint方式。
(3)       提供了较为简单的Validation Adaptor方式进行校验
(4)       试图提供一个Validation Client Context来规避不必要的Constraint
(5)       提供了便于管理和维护的Validation Service类
 
    在eclipse所提供的帮助站点上,提供了对Validation Framework的简要介绍,以及简单的Examples。
    http://help.eclipse.org/help32/topic/org.eclipse.emf.validation.doc/tutorials/validationTutorial.html
    但是这个tutorial并没有对EMF Validation进行太深入地介绍,仅仅只是介绍了比较浅显的“How to use it”,而没有深入的介绍Validation的构架、对象逻辑等等。
 
    为了便于理解EMF Validation,将主要的Component Diagram绘制如下。这个组件图要抽象了很多,其实里面的组件有些是相当的复杂的。比如Constraint Parser和Model Constraint。
 

时间不早了,有关组件的详细解释先不写了,留着下篇吧。不过这个组件图应该可以帮助大家在阅读Validation代码的时候起到一定辅助作用。 围绕EMF探索(5)之深入Validation框架

 

    在EMF的eCore框架中,本身提供了对Validation Framework的支持,而EMFT的Validation组件则是在这个基础上又扩展的大量的功能。如果大家采用Validator Adaptor方式,可能会更加体会到对Evalidator的应用。
 
    但是由于EValidator是在注册过程中,是依据EPackage来匹配的,针对一个ePackage一般只能注册一个Evalidator对象。这就限制的应用的扩展性。
EValidator.Registry.INSTANCE.put(
                            LibraryPackage.eINSTANCE, new LibraryValidator());
 
    而EMFT的Validation Framework则在这个基础之上进行了扩展,但是Validation Framework没有在EObjectValidator的基础上进行扩展,而是另辟蹊径,构造了自己的一套实现构架,甚至完全抛弃了EMF eCore所提供的DiagnosticChain机制,而是采用eclipse runtime IStatus对象来记录校验的结果。
 
    为了便于理解,绘制了一张EMFT Validation Framework的主要构思图:
 
    整个EMFT Validation Framework的核心就是两个概念:IValidator和Constraint。其中IValidator是有别于EMF eCore的EValidator。IValidator是一个验证执行器,为了支持对Batch和Live两种模式的支持,所以有不同的接口和实现类。Batch模式就是可以对批量对象进行验证,而Live模式则可以在对象值变更的时候相应验证。
    IValidator执行器会从Validation Service模块中获取所匹配的Constraint进行验证,当然,为了优化和便于管理,Validation Framework还提供了对Context、Binding、ProviderOperation等方面的支持。不论如何,最终的解决目的就是为了找出合适的Constraint进行验证。
    有关Constraint的代码,几乎占据了Validation Framework代码量的大部分,其实解决的目的就是为了可以方面的支持多种Constraint Model,目前支持三种方式:Java Code,EMF Model,以及OCL。
 
    在Validation Framework构架中,真正用于constraint validate是ImodelConstraint接口,不同的Constraint Model类型下会有不同的实现。
    因为Validation Framework这套构架依赖于在plugin.xml中公国描述和申明来注册相应的constrain实现,所以需要不同的Parser负责解析和管理。看看下面的类图,应该就比较清晰了。
 
    当然,在我们使用Validation Framework这套框架的过程中,是不会接触到 这些parser的,甚至根本不知道IModelConstraint的存在。
    比如,针对java模式,一般我们会继承一个AbstractModelConstraint类来实现。如下图所示:
 
 
    事实上,这是一个很简单的Adapter模式的应用,具体就没有必要细说了,类图已经很清晰的反映了一切。
 

组织简介 | 联系我们 |   Copyright 2002 ®  UML软件工程组织 京ICP备10020922号

京公海网安备110108001071号