OSGi和Hibernate(2007-06-04)翻译:javavsnet 审校:BlueDavy 我的一个荷兰老朋友,Petr van Blokland 和我正在研发另一个web 框架。我不会深入讨论为什么要开发这个web框架(已经有了如此多的web框架),只想说明他的背景是平面造型设计,这意味着美学在这个框架中起着重要的作用。然而,这点并不会让我们忽略对于web框架的细节处理,例如web请求,模型,视图和数据库。我参与这个项目是因为这是得到一些企业系统经验的好方法。当然整个软件的架构是基于OSGi的。 为了得到框架的轮廓,我介绍一些架构的片段。进来的请求通过 Host header被分派到一个servlet的一个服务。这使得在同一个OSGI框架中处理多个站点成为可能。我们把这个servlet称为站点 servlet。这个我们向其分派请求的servlet是任意的,但是到目前为止使用一个手工编写的servlet,它使用Groovy类作为页面。路径的第一部分是类名,第二部分是方法名,其余的是参数。这就是说,一个类似http://www.acme.com/home/index 的请求是一个对在 home类中的方法index()的调用。 Groovy类是个正常的类,不需要扩展其他类或者实现任何接口。当我们创建它时,我们分配给它一个特殊的元类,我们称这个元类为构造器。它和一个Groovy构造器略有不同。但是试图达到同样的目标,合并代码和 html。站点servlet建立这个构造器。每个站点可以有自己特殊的构造器。构造器能够为页面类提供方法,这使得我们可以方便的为页面提供高级别的标签。例如,你可以为一个导航条提供你自己的标签。 在Groovy中构造器是个绝妙的概念,它们融合了一个完全动态语言的能量和说明式编程(HTML)的能量。了达到这个目标使用了闭包。闭包是可以访问他们周围上下文的代码块,但是他们可以象对象一样被操作。 def x = { print "hello $it" } x() // 调用块 作为一个Smalltalk程序员,Java中缺乏闭包是在我的职业生涯中最痛苦的经历之一。无论如何,闭包允许你确定函数的参数。在Groovy中,闭包允许你写这样的代码: def index() { def title = "Hello World" html { head { title { title } } body { h1 { title } } } } 这确实是一个非常有趣的方法,但是这不是这篇blog的目的。我们的web框架依然需要一个数据库......Groovy提供了一些非常有趣的SQL能力而我真的被吸引了。然而,Groovy使用了一些神秘的技巧,在运行时重编译了源代码使得不需要写SQL语句(参见findAll)。一个崇高的目标,但是在这种情况下好处不值得付出的代价。当源代码在运行时不可用时,代码会生成怪异的错误。所以唯一的选择看起来是Hibernate。 Hibernate 是一个对象关系映射器。它允许你写简单的可以被持久化在数据库中的Java对象。对象不需要继承任何父类或实现任何接口。所有的映射信息在xml文件中详述,该文件与类文件是相邻的。就是说,当hibernnate必须映射一个类到一个关系数据库时,它使用类名创建一个资源路径,然后把那个资源路径作为它的映射文件装载进来。然而,这个映射文件必须在数据库连接建立起来之前被读取。 Hibernate操作类路径和其他一些通常不会和基于OSGI的系统良好合作的程序。原因是在许多系统中类在模块间的可见性或多或少是无限制的。在OSGI框架中,类路径是被良好定义和限制的。这给了我们许多好的特征,但是当我们需要这样一个类库,随着该类库的成长它强烈希望成为一个类装载器时,OSGI框架对类路径的良好定义和限制也给我们带来痛苦。 我们需要一个数据库,所以在过去几周我创建了一个bundle作为OSGI中的bundle 和领域对象的中介,负责连接到数据库和bundle对数据库的使用。实际上,让这些都能工作起来是非常辣手的,我不确信我已经找到了解决方案。让大家看看我做了什么,也让我知道你们的想法。 ![]() 要和Hibernate一起工作,你需要一个会话对象。你可以从一个会话工厂得到一个会话对象。为了得到会话工厂,你需要用一个配置对象来创建它。配置对象从一个配置XML文件创建。Hibernate缺省的从你的JAR文件的根路径装载该配置文件,然而,如果需要的话你可以手工将类添加到配置中。 这带给我们一个问题。我们有几个bundle需要使用数据库,但是不能先验的决定在任意时刻哪个bundle是可用的。他们可以都使用他们自己的Hibernate会话工厂但是这会显著的使配置管理变得复杂而且影响性能。 这看起来象一个明显的扩展模型问题 。Bundle应该能够声明它们贡献到Hibernate中的类。 对于这个模型,我设计了下列manifest header: Hibernate-Contribution ::= default; classes="xierpa.impl.pw.User,xierpa.impl.pw.Role" header 中的名称字段(default)是数据库contribution的名称。classes属性包含一个由逗号隔开的类的列表。这些类是从bundle中装载进来的然后加入配置的,该配置使用这个contribution。简单。现在任何类都可以通过声明这个manifest header来向Hibernate提供领域类。 然而,会话工厂配置从何而来?我决定使用配置Admin来做这个工作。对于每个配置可以创建一个工厂配置。针对这个配置的属性描述了数据库连接参数。配置太大了,以至于我不得不扩展FileInstall,以便不仅仅安装bundle,而且安装配置文件。正常的有命令行的交互是非常基础的。 因此,Hibernate扩展可以接收到配置并且跟踪开始的bundle。有了这些信息,Hibernate就可以将从任何bundle到配置的contribution与会话工厂匹配了。 想要得到一个Hibernate会话工厂对象的bundle只要得到HibernateDomain服务,并且在它需要一个数据库事务时请求一个会话。这个会话从一个工厂得到,工厂在一个contribution变化或者配置参数变化时自动刷新。 总的来说这个模型工作的很好。bundle可以容易的提供contribution而且可以容易的使用一个新鲜的Hibernate会话而不需要去跟踪配置和contribution。 当然,我也碰到了一个龌龊的类装载器问题。Hibernate使用从Hibernate bundle得到的类来创建一个代理。不幸的是,它让领域对象的类装载器来装载这个Hibernate特有的类。“哦,当开始类装载时我们编织了多么混乱web......” 显然我一直使用bnd而且bnd仅仅为代码真的使用了的包插入导入包的语句。Hibernate的一个伟大的优点是它一直允许你使用与Hibernate没有任何耦合的对象。显然,bnd因此可以不插入任何引用。 这个问题的短期解决方案是Require-Bundle,本blog的读者可以证明它当然不是一个好的解决方案。它解决了在这种情况下的问题,但是从长远来看产生了许多其他问题。在OSGI规范的下一个版本中我们必须找到这个问题的解决方案。我们必须可以创建清晰的bundle,它们仅仅需要导入它们所需要的类,但是仍然装载其他bundle使得他们需要装载的类。这个问题已经在CPEG提出了,它得到了高度关注,因为对于其他类库也出现了同样的问题。 我非常有兴趣想知道其他人如何解决在OSGI服务平台中使用象Hibernate这样的类库的问题。请提供反馈。 Peter Kriens |
|
|
Home | Trademark Policy | Privacy Policy Copyright © 2010 OSGi™ Alliance. Comments about the site? Send them to: OSGi Alliance WebMaster. |