软件建模E04:EMF最小工具链,Ecore与XMI的真实往返
一份 XML 能被解析,并不说明其中的预留对象满足必选引用;一组预留满足 Ecore 的结构约束,也不说明它们没有占用同一设备的重叠时段。模型工具解决了哪些问题,需要通过实际装载、诊断和往返结果分别判断。
本篇使用 Eclipse EMF 的真实运行库读取租赁元模型与实例,制造缺少编号、缺少引用和时段重叠三个反例,然后保存元模型与实例,在新的资源集合中重新加载。业务规则由明确的 Java 检查补充,没有把自制 JSON 程序称为 EMF,也没有运行 Xtext 或 OCL。
固定的是一组组件版本
实验固定 org.eclipse.emf.common 2.42.0、org.eclipse.emf.ecore 2.39.0 与 org.eclipse.emf.ecore.xmi 2.39.0。三个组件的版本号不要求相同;Ecore 2.39.0 的发布 POM 明确依赖 Common 2.42.0,XMI 2.39.0 则依赖 Ecore 2.39.0。Ecore发布POM
下载来源为 Maven Central 的对应固定坐标。每个 jar 与仓库公开 SHA1 对照后,再锁定 SHA256;运行入口对缓存同样核验摘要。此次选择是历史版本组合,不宣称代表最新 EMF,实际 Java 环境为 Corretto 21.0.11。
这条路径直接使用三个运行库,Java 检查程序通过 javac 编译到临时目录,再由 java 执行。它不安装 Eclipse IDE,也不依赖 Maven 插件解析或本地已有的生成代码。POM 与 jar 清单都保存为证据,第三方 jar 只保留在外部缓存。
元模型定义对象形状与引用
rental.ecore 声明 Fleet、Equipment 和 Reservation 三个类。Fleet 包含设备和预留,Equipment 有编号与状态,Reservation 有编号、设备引用、起止位置。编号与设备引用的下界为一,用来表达必选字段和关系。
起止值在这个教学样本中只是整数位置,不是 Java Instant,也没有时区或金额。状态暂用字符串,元模型没有声明合法状态枚举或迁移关系。因此,把 ACTIVE 写进实例仅表示一个字段值,不会让 EMF 自动实现审批和退役行为。
Eclipse 的 EMF 说明把 Ecore 列为描述模型的元模型,并提供反射访问、通知与默认 XMI 持久化支持。本篇使用其中的动态对象和资源读写能力,没有执行 Java 类生成器。EMF项目说明
flowchart TB
META["rental.ecore<br/>类、属性、引用、下界"] --> REG["ResourceSet加载并注册EPackage"]
XMI["rental.xmi<br/>E1与R1,R1引用E1"] --> LOAD["加载动态EObject"]
REG --> LOAD
LOAD --> STRUCT["Diagnostician结构校验"]
STRUCT --> POLICY["本篇Java检查:区间与重叠"]
POLICY --> SAVE["保存Ecore和XMI"]
第一张图中的包注册把命名空间 URI 关联到已加载的 EPackage。实例文档通过该命名空间找到类定义;扩展名工厂负责创建相应资源。两者共同确定如何读入文档,不是只把 XML 标签转成任意字典。
包含关系与业务引用不同
Fleet 的 equipment 和 reservations 都是包含关系,决定对象属于哪棵模型内容树。Reservation 指向 Equipment 的关系则是普通引用,表示预留针对哪台设备。预留并不因为引用设备就取得设备的模型包含权。
输入只有 E1 和 R1,R1 的设备引用使用 E1 的编号。加载后,实验检查该引用恰好指向 Fleet 设备集合中的那个 EObject,而不只是得到一个内容相同的副本或字符串。这使后续同设备判断能够针对同一个模型对象。
编号属性被标记为 ID,序列化时可用于引用定位。模型身份、内存对象身份和文件引用形式仍有区别:重新加载产生新的 Java 对象,但 R1 在新对象图中的设备引用仍应落到同一个新 E1。往返不能要求旧对象地址继续存在。
结构诊断不会自动理解占用政策
实验先调用 Diagnostician.INSTANCE.validate 检查元模型和合法实例,结果严重级别为零。随后从实例复制出负例,将 E1 的编号移除,诊断返回 ERROR;再移除 R1 的设备引用,同样得到 ERROR。两项原始诊断分别保存到文件。
这里显式调用了诊断器。成功加载或保存,不应被当成已经运行所有业务校验的证据;程序需要决定在何处检查、遇到何种严重级别停止。编辑器中允许暂时不完整的对象,也不等于交付时可以接受缺少必选引用。
第三个反例增加 R2,仍引用 E1,时间段为 [2,4);R1 的时间段为 [1,3)。两个编号不同,字段齐全,引用合法,所以默认结构诊断继续通过。但业务检查比较同设备的区间,返回 OVERLAP。
把 R2 改成 [3,4) 后通过,因为两个左闭右开区间只是相邻;再把结束位置改成 3,检查返回 INVALID_PERIOD。这些判断由本篇手写 Java 函数执行,并未嵌入 Ecore 的声明,也不是 OCL 表达式的求值结果。
往返究竟比较什么
合法元模型复制后写入 roundtrip.ecore,合法实例复制后写入 roundtrip.xmi。随后创建全新的 ResourceSet,重新加载刚保存的 Ecore、注册包,再读取刚保存的实例。新旧包与根对象都被检查为不同内存对象,排除了只是继续使用原对象的假往返。
flowchart LR
A["原ResourceSet<br/>EPackage与EObject图"] --> W["EMF保存<br/>roundtrip.ecore、roundtrip.xmi"]
W --> B["新ResourceSet<br/>重新加载包和实例"]
B --> C["结构诊断与业务检查再次通过"]
B --> D["比较设备/预留事实"]
B --> E["核对R1引用新图中的E1"]
第二张图没有以文件字节相同作为业务判定。比较程序提取设备编号、状态以及预留编号、设备编号、起止值,排序后逐项对照;得到相同的事实列表。引用一致性另行检查,避免仅凭打印出的编号相同掩盖对象图错误。
这证明当前实例经过这组工具保存、重载后保留了选定业务事实。它没有证明所有 Ecore 特性、跨文件代理、继承、多态或自定义数据类型都能无损往返;样本中未出现的机制不能由这次成功推断。
保存资源也没有执行模型升级。若下一个版本改变必选属性、编号或引用目标,旧 XMI 是否还能读取,需要新的迁移规则和负例。读写同一个元模型版本,与在两个领域版本之间转换是不同验收任务。
EMF、Xtext和OCL分别承担什么
本篇选择的 EMF Core 路径提供元模型、动态对象、资源和序列化基础。手写 Java 程序是测试驱动与业务检查器,不是生成的租赁应用。能够读写 EObject,也不意味着已经有适合业务人员使用的编辑界面。
Xtext 的官方教程从文法生成语言基础设施和文本编辑器,涉及解析、链接、校验及编辑服务;本例直接写 Ecore 和 XMI,不需要这一层文本 DSL 工具。Xtext官方入门
Eclipse OCL 提供针对 Ecore 或 UML 模型解析、求值约束与查询的能力。若选择它,可以把某些业务约束写成专门语言,但还要配置求值环境与调用时机。当前重叠检查明确在 Java 中,不能因为模型来自 EMF 就声称使用了 OCL。Eclipse OCL项目说明
模型转换则需要另一个源到目标的规则。这里完成的是同一元模型下的序列化往返,没有把 PIM 转成某个平台的 PSM,也没有生成服务接口。把这些职责分别记录,才能准确说明引入工具后减少了哪一类手写代码。
工具合同与业务合同的边界
lowerBound=1 能让诊断器发现编号缺失,但不表达租赁编号应当遵循怎样的业务编码规则。字符串类型也不会自动拒绝未知设备状态。若真实入口允许任意输入,还要添加字符串内容、枚举取值与状态变化方面的约束;“元模型合法”不等于所有业务假设都已经编码。
同样,包含树解决对象在资源中的组织,普通引用解决对象间的连接,两者都没有数据库事务语义。把两项重叠预留装进内存后才发现冲突,与多个并发请求能否原子地取得承诺是不同问题。本例没有模拟并发,也没有把校验器当成锁或事务管理器。
业务检查函数假设结构已经有效。它读取设备引用和整数起止位置,不负责再次诊断每一种残缺对象。这样能看清各层职责,但调用方必须保持校验顺序;绕过结构诊断直接向业务函数传入不完整对象,不属于本例承诺的输入合同。
往返比较采用显式事实投影,也有维护成本。如果元模型增加了押金、审批人或时间单位,比较函数没有同步加入新字段,就可能漏掉新增信息的丢失。模型变化时需要同时更新样本、事实投影与负例,而不是只确认文件还能重新打开。
当前运行只需要 POM 中的非可选依赖;平台运行时等可选组件没有加入类路径。若后续转向 Eclipse 编辑器或生成器,依赖范围可能扩展。复用这三个 jar 的清单只能复现本篇动态模型路径,不能据此承诺任意 EMF 插件都可以独立运行。
复跑与限制
1 | |
运行需要 Java 21 的 java、javac,以及 Python 标准库和 Bash。可通过 JAVA_HOME 指定 JDK,通过 MODELING_TOOL_CACHE 指定 jar 缓存;首次运行从固定 Maven Central 地址下载并校验,已有缓存也会检查 SHA256。
实际完成 17 个场景,编译与运行退出码均为 0。jar-manifests.json、版本输出、编译日志、结构诊断、往返文件及 semantic-facts.txt 都保存在 evidence/E04/。输入是仓库内可信模型,本例不提供面向任意外部 XML 的安全导入服务。






