如何使用 OSGi 模块化 JSF/Facelets/Spring 应用程序?
我正在处理非常大的 JSF/Facelets 应用程序,它们使用 Spring 进行 DI/bean 管理。 我的应用程序具有模块化结构,我目前正在寻找标准化模块化的方法。
我的目标是由多个模块(可能相互依赖)组成一个 Web 应用程序。每个模块可能包含以下内容:
- 类;
- 静态资源(图像、CSS、脚本);
- Facelet 模板;
- 托管 bean - Spring 应用程序上下文,带有请求、会话和应用程序范围的 bean(或者是 JSF 托管 bean);
- Servlet API 内容 - servlet、过滤器、侦听器(这是可选的)。
我想避免(几乎不惜一切代价)的是需要将模块资源(如 Facelets 模板)复制或提取到 WAR 或扩展模块的 servlet、过滤器的 web.xml
等等。将模块(JAR、捆绑包、工件等)添加到 Web 应用程序(WEB-INF/lib
、bundles
、plugins
, ...) 来使用此模块扩展 Web 应用程序。
目前,我使用自定义模块化解决方案来解决此任务,该解决方案很大程度上基于使用类路径资源:
- 特殊资源 servlet 提供来自类路径资源 (JAR) 的静态资源。
- 特殊的 Facelets 资源解析器允许从类路径资源加载 Facelet 模板。
- Spring 通过 classpath*:com/acme/foo/module/applicationContext.xml 模式加载应用程序上下文 - 这会加载模块 JAR 中定义的应用程序上下文。
- 最后,一对委托 servlet 和过滤器将请求处理委托给模块中在 Spring 应用程序上下文中配置的 servlet 和过滤器。
最近几天,我阅读了很多有关 OSGi 的内容,并且正在考虑如何(以及是否)使用 OSGi 作为标准化模块化方法。我正在考虑如何使用 OSGi 解决各个任务:
- 静态资源 - 想要导出静态资源的 OSGi 包在包上下文中注册一个
ResourceLoader
实例。中央ResourceServlet 使用这些资源加载器从包中加载资源。 - Facelet 模板 - 与上面类似,中央
ResourceResolver
使用由捆绑包注册的服务。 - 托管 bean - 如果
myBean
在其中一个包中定义,我不知道如何使用#{myBean.property}
这样的表达式。 - Servlet API 内容 - 使用 WebExtender/Pax Web 之类的东西来注册 servlet、过滤器等。
我的问题是:
- 我在这里发明了自行车吗?有标准解决方案吗?我发现提到了 Spring Slices,但找不到太多关于它的文档。
- 您认为 OSGi 是完成所描述任务的正确技术吗?
- 我的 OSGI 应用程序草图或多或少正确吗?
- 应该如何处理托管 bean(尤其是请求/会话范围)?
我通常会感谢您的评论。
I'm working with very large JSF/Facelets applications which use Spring for DI/bean management.
My applications have modular structure and I'm currently looking for approaches to standardize the modularization.
My goal is to compose a web application from a number of modules (possibly depending on each other). Each module may contain the following:
- Classes;
- Static resources (images, CSS, scripts);
- Facelet templates;
- Managed beans - Spring application contexts, with request, session and application-scoped beans (alternative is JSF managed beans);
- Servlet API stuff - servlets, filters, listeners (this is optional).
What I'd like to avoid (almost at all costs) is the need to copy or extract module resources (like Facelets templates) to the WAR or to extend the web.xml
for module's servlets, filters, etc. It must be enough to add the module (JAR, bundle, artifact, ...) to the web application (WEB-INF/lib
, bundles
, plugins
, ...) to extend the web application with this module.
Currently I solve this task with a custom modularization solution which is heavily based on using classpath resources:
- Special resources servlet serves static resources from classpath resources (JARs).
- Special Facelets resource resolver allows loading Facelet templates from classpath resources.
- Spring loads application contexts via the pattern
classpath*:com/acme/foo/module/applicationContext.xml
- this loads application contexts defined in module JARs. - Finally, a pair of delegating servlets and filters delegate request processing to the servlets and filters configured in Spring application contexts from modules.
Last days I read a lot about OSGi and I was considering, how (and if) I could use OSGi as a standardized modularization approach. I was thinking about how individual tasks could be solved with OSGi:
- Static resources - OSGi bundles which want to export static resources register a
ResourceLoader
instances with the bundle context. A centralResourceServlet
uses these resource loaders to load resources from bundles. - Facelet templates - similar to above, a central
ResourceResolver
uses services registered by bundles. - Managed beans - I have no idea how to use an expression like
#{myBean.property}
ifmyBean
is defined in one of the bundles. - Servlet API stuff - use something like WebExtender/Pax Web to register servlets, filters and so on.
My questions are:
- Am I inventing a bicycle here? Are there standard solutions for that? I've found a mentioning of Spring Slices but could not find much documentation about it.
- Do you think OSGi is the right technology for the described task?
- Is my sketch of OSGI application more or less correct?
- How should managed beans (especially request/session scope) be handled?
I'd be generally grateful for your comments.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
您的目标听起来是可行的,但有一些注意事项:
视图层:首先,您的视图层听起来有点臃肿。还有其他方法可以通过使用自定义组件来模块化 JSF 组件,从而避免尝试创建像后期绑定托管 bean 这样引人注目的东西所带来的麻烦。
模块本身:其次,你的模块看起来并不是特别模块化。您的第一个项目符号列表听起来好像您正在尝试创建可互操作的 Web 应用程序,而不是模块本身。我对模块的想法是,每个组件都有一个明确定义的、或多或少离散的用途。就像 ex 是 vi 的基础一样。如果您要走 OSGi 路线,那么我们应该像这样定义模块化:模块化,为了讨论的目的,意味着组件是可热插拔的 - 也就是说,它们可以添加和删除而不破坏应用程序。
依赖关系:我对您将模块描述为“可能相互依赖”感到有点担心。您可能(我希望)已经知道这一点,但是您的依赖关系应该形成一个有向无环图。一旦引入循环依赖,您就会对应用程序的最终可维护性造成巨大的伤害。 OSGi 最大的弱点之一是它不能防止循环依赖,因此由您来强制实施这一点。否则,您的依赖关系将像野葛一样增长,并逐渐阻塞系统生态系统的其余部分。
Servlet: Fuhgeddaboudit。在 Servlet 3.0 规范投入生产之前,您不能将 Servlet 后期绑定到 Web 应用程序中(正如 Pascal 指出的那样)。要启动单独的实用程序 servlet,您需要将其放入其自己的应用程序中。
好的,注意事项就这么多。让我们考虑一下它是如何工作的:
您已经定义了自己的 JSF 模块来执行...具体是什么?让我们给它一个明确的、相当琐碎的目的:登录屏幕。因此,您创建了登录屏幕,使用 OSGi 将其后期绑定到您的应用程序中,然后……然后呢?如果您没有在 .jspx 页面中定义登录功能,应用程序如何知道登录功能存在?应用程序如何知道导航到它不知道的东西?
有一些方法可以使用条件包含等来解决这个问题(例如
),但是,就像你说的,当您的托管 loginBean 存在于另一个模块中,该模块甚至可能尚未引入到应用程序中。事实上,除非该 loginBean 存在,否则您将收到 servlet 异常。那你怎么办?您在其中一个模块中定义一个 API。您打算在模块之间共享的所有托管 Bean 必须指定为该 API 层中的接口。并且您的所有模块都必须具有它们打算使用的任何这些接口的默认实现。并且这个API必须在所有可互操作的模块之间共享。然后,您可以使用 OSGi 和 Spring 将指定的 bean 及其实现连接在一起。
我需要花一点时间指出,这不是我处理这个问题的方式。一点也不。对于像登录页面这样简单的东西,甚至像股票图表一样复杂的东西,我个人更喜欢创建一个自定义 JSF 组件。但是,如果需求是“我希望我的托管 bean 是模块化的(即可热插拔等)”,那么这是我知道使其工作的唯一方法。我什至不完全确定它会起作用。 此电子邮件交换表明这是 JSF 的问题开发人员才刚刚开始工作。
我通常认为托管 bean 是视图层的一部分,因此我仅将它们用于视图逻辑,并将其他所有内容委托给服务层。在我看来,使托管 bean 后期绑定是将它们从视图层提升到业务逻辑中。所有这些教程都如此关注服务是有原因的:因为大多数时候您想要考虑您的应用程序“无头”运行需要什么,以及“剥皮”您的视图有多么容易,如果,例如,您希望它能够在 Android 手机上运行及其所有功能。
但听起来您正在处理的很多内容本身就是视图逻辑——例如,需要交换不同的视图模板。 OSGi/Spring 应该能够提供帮助,但是您需要应用程序中的一些东西来在可用的实现之间进行选择:几乎就是 OSGi 的服务注册表的构建目的。
这就留下了静态资源。您可以将它们模块化,但请记住,您需要定义一个接口来检索这些资源,并且需要提供默认实现,以便您的应用程序在它们不存在时不会出现阻塞。如果考虑国际化,这可能是一个不错的选择。如果您真正想要冒险,那么您可以将静态资源推送到 JNDI 中。这将使它们完全可热插拔,并节省您尝试以编程方式解决使用哪个实现的痛苦,但也有一些缺点:任何失败的查找都会导致您的应用程序抛出 NamingException。这太过分了。 JNDI 通常在 Web 应用程序中用于应用程序配置。
至于您剩下的问题:
你是,一点点。我见过执行此类操作的应用程序,但您似乎无意中遇到了一组相当独特的要求。
如果您需要模块可热插拔,那么您的选择是 OSGi 和轻量级的 ServiceLocator 接口。
如果不了解更多关于组件边界的信息,我真的无法判断。目前,听起来您可能正在推动 OSGi 做超出其能力的事情。
但不要相信我的话。 我 找到 其他这些地方的阅读材料。
既然您询问 Spring Slices,这个应该足以让您开始。您将需要一个 Git 客户端,并且看起来您将通过查看源代码来培训自己使用该应用程序。这是非常早期的原型代码。
What you're aiming to do sounds doable, with a few caveats:
The View Layer: First, your view layer sounds a little overstuffed. There are other ways to modularize JSF components by using custom components that will avoid the headaches involved with trying to create something as dramatic as late-binding managed beans.
The Modules Themselves: Second, your modules don't seem particularly modular. Your first bullet-list makes it sound as if you're trying to create interoperable web apps, rather than modules per se. My idea of a module is that each component has a well-defined, and more or less discrete, purpose. Like how ex underlies vi. If you're going down the OSGi route, then we should define modular like this: Modular, for the sake of this discussion, means that components are hot-swappable -- that is, they can be added and removed without breaking the app.
Dependencies: I'm a little concerned by your description of the modules as "possibly depending on each other." You probably (I hope) already know this, but your dependencies ought to form a directed acyclic graph. Once you introduce a circular dependency, you're asking for a world of hurt in terms of the app's eventual maintainability. One of the biggest weaknesses of OSGi is that it doesn't prevent circular dependencies, so it's up to you to enforce this. Otherwise your dependencies will grow like kudzu and gradually choke the rest of your system's ecosystem.
Servlets: Fuhgeddaboudit. You can't late-bind servlets into a web app, not until the Servlet 3.0 spec is in production (as Pascal pointed out). To launch a separate utility servlet, you'll need to put it into its own app.
OK, so much for the caveats. Let's think about how this might work:
You've defined your own JSF module to do... what, exactly? Let's give it a defined, fairly trivial purpose: a login screen. So you create your login screen, late-bind it using OSGi into your app and... then what? How does the app know the login functionality is there, if you haven't defined it in your .jspx page? How does the app know to navigate to something it can't know is there?
There are ways to get around this using conditional includes and the like (e.g.,
<c:if #{loginBean.notEmpty}>
), but, like you said, things get a little hairy when your managed loginBean exists in another module that may not have even been introduced to the app yet. In fact, you'll get a servlet exception unless that loginBean exists. So what do you do?You define an API in one of your modules. All the managed beans that you intend to share between modules must be specified as interfaces in this API layer. And all your modules must have default implementations of any of these interfaces that they intend to use. And this API must be shared between all interoperable modules. Then you can use OSGi and Spring to wire together the specified beans with their implementation.
I need to take a moment to point out that this is not how I would approach this problem. Not at all. Given something like as simple as a login page, or even as complicated as a stock chart, I'd personally prefer to create a custom JSF component. But if the requirement is "I want my managed beans to be modular (i.e., hot-swappable, etc)," this is the only way I know to make it work. And I'm not even entirely sure it will work. This email exchange suggests that it's a problem that JSF developers have only just started to work on.
I normally consider managed beans to be part of the view layer, and as such I use them only for view logic, and delegate everything else to the service layer. Making managed beans late-binding is, to my mind, promoting them out of the view layer and into the business logic. There's a reason why all those tutorials are so focused on services: because most of the time you want to consider what it would take for your app to run "headless," and how easy it would be to "skin" your view if, for instance, you wanted it to run, with all its functionality, on an Android phone.
But it sounds like a lot of what you're working with is itself view logic -- for instance, the need to swap in a different view template. OSGi/Spring should be able to help, but you'll need something in your app to choose between available implementations: pretty much what OSGi's Service Registry was built to do.
That leaves static resources. You can modularize these, but remember, you'll need to define an interface to retrieve these resources, and you'll need to provide a default implementation so your app doesn't choke if they're absent. If i18n is a consideration, this could be a good way to go. If you wanted to be really adventurous, then you could push your static resources into JNDI. That would make them completely hot-swappable, and save you the pain of trying to resolve which implementation to use programmatically, but there are some downsides: any failed lookup will cause your app to throw a NamingException. And it's overkill. JNDI is normally used in web apps for app configuration.
As for your remaining questions:
You are, a little. I've seen apps that do this kind of thing, but you seem to have stumbled into a fairly unique set of requirements.
If you need the modules to be hot-swappable, then your choices are OSGi and the lighter-weight ServiceLocator interface.
I can't really tell without knowing more about where your component boundaries are. At the moment, it sounds like you may be pushing OSGi to do more than it is capable of doing.
But don't take my word for it. I found other reading material in these places.
And since you ask about Spring Slices, this should be enough to get you started. You'll need a Git client, and it looks like you'll be training yourself on the app by looking through the source code. And it's very early prototype code.
我在当前的项目中面临着同样的问题。在我看来,就标准和未来支持而言,OSGi 是最好、最干净的解决方案,但目前如果您尝试在 Web 应用程序中使用它,您可能会遇到一些问题:
基于类加载器和资源过滤器的自定义解决方案似乎非常合适为我。
作为示例,您可以研究 Hudson 源代码 或 Java 插件框架(JPF)项目(http://jpf.sourceforge.net/)。
至于扩展 web.xml,我们可能很幸运有 Servlet 3.0 规范(http://today.java.net/pub/a/today/2008/10/14/introduction-to-servlet-3.html#可插拔性和可扩展性)。
I am facing the same problems in my current project. In my opinion, OSGi is the best and cleanest solution in terms of standards and future support, but currently you may hit some problems if you try using it in a web application:
A custom solution based on class loaders and resource filters seems very appropriate for me.
As an example you can study the Hudson source code or Java Plug-in Framework (JPF) Project(http://jpf.sourceforge.net/).
As about extending the web.xml, we may be lucky with the Servlet 3.0 specification(http://today.java.net/pub/a/today/2008/10/14/introduction-to-servlet-3.html#pluggability-and-extensibility).
Servlet 引入的“Web 模块部署描述符片段”(又名 web-fragment.xml) 3.0 规范 在这里会很好。规范将其定义为:
不过,Java EE 6 目前可能不适合您。尽管如此,它仍然是标准化的解决方案。
The "web module deployment descriptor fragment" (aka web-fragment.xml) introduced by the Servlet 3.0 specification would be nice here. The specification defines it as:
Java EE 6 is maybe not an option for you right now though. Still, it would to be the standardized solution.
Enterprise OSGi 是一个相当新的领域,因此不要认为您会得到直接满足您需求的解决方案。也就是说,我发现 Equinox 缺少的一件事(Eclipse 背后的 osgi 引擎,因此拥有最大的用户群!)是一致的配置/DI 服务。在我最近的项目中,我们有一些类似的需求,并结束了构建一个简单的配置 osgi 服务。
模块化应用程序固有的问题之一是 DI,因为模块可见性在某些情况下可能会阻止类访问。我们使用注册好友策略解决了这个问题,虽然不太理想,但很有效。
除了配置之外,您还可以查看最近发布的 Equinox 书籍,以获取有关使用 OSGi 作为创建模块化应用程序的基础的指南。这些示例可能特定于 Equinox,但其原理适用于任何 OSGi 框架。链接 - http://equinoxosgi.org/
Enterprise OSGi is a fairly new domain so dont think you will get a solution that directly satisfies your need. That said one of the things I found missing from Equinox (osgi engine behind eclipse and hence one with largest user base!) is a consistent configuration / DI service. In my project recently we had some similar needs and ended building a simple configuration osgi service.
One of the problems which will be inherent to modular applications would be around DI, as the module visibility could prevent class access in some cases. We got around this using a registered-buddy policy, which is not too ideal but works.
Other than configuration, you can take a look at the recently released Equinox book for guidance on using OSGi as base for creating modular applications. The examples may be specific to Equinox, but the principles would work with any OSGi framework. Link - http://equinoxosgi.org/
您应该研究一下 Spring DM Server(它正在转换为 Eclipse Virgo,但尚未发布)。最近刚刚发布的 OSGi 企业规范中有很多好东西。
我想一些 Spring DM 教程会有帮助。但是,是的,可以使用标准模块化从 Web 包外部加载资源和类。从这一点来说,这是一个很好的契合。
至于会话上下文 - 它会按照您在会话中所期望的方式进行处理。但是,您可能会遇到在 Web 捆绑包之间共享该会话的问题,甚至不确定是否可能。
您还可以寻求单个 Web 捆绑包,然后使用 Eclipse 扩展注册表等来扩展 Web 应用程序的功能。
You should look into Spring DM Server (it's being transitioned to Eclipse Virgo but that's not been released yet). There's a lot of good things in the recent OSGi enterprise spec which has also just been released.
Some of the Spring DM tutorials will help, I'd imagine. But yes, it's possible to have both resources and classes loaded from outside the web bundle using standard modularity. In that, it's a good fit.
As for the session context - it gets handled as you would expect in a session. However, you might run into problems with sharing that session between web bundles to the extent that in not sure if it's even possible.
You could also look to have a single web bundle and then use e.g. the Eclipse extension registry to extend the capabilities of you web app.