2

我有一个 Web 应用程序大量使用 jspx-files 和jsp:include-includes,它从同一个 jar 部署到 tomcat 中的数百个上下文。目前我们只能通过更改 CSS 或覆盖其他文件目录中的某些文件来修改每个实例的布局:

假设 webapp.jar 包含一个名为的文件/css/forms.css,那么 webapp 确保将 GETtinghttp://mydomain/cust1/res/css/forms.css映射到该文件。如果 webapp 被配置为在 "override directory" 中查找/data/cust1/,那么如果有一个名为/data/cust1/css/forms.css该文件的文件,则会从应用程序存档中的文件提供该文件。这是由 webapp 本身确保的。

在过去的几年里,这非常成功,但最近我感到了限制不能“覆盖”的或多或少的静态 jspx 的痛苦;)基本上我希望能够为每个文件“覆盖”一些 jspx 文件无需为每个上下文编译和部署自定义 webapp-jar 即可部署上下文。(拥有自定义something:includeJsp标签不会有问题)。

基本上,webapp 应该能够为单个 jspx 文件提供对 jsp(x) 编译器的覆盖,例如,看看下面的示例结构:

<!-- webapp.jar:/jspx/view.jspx -->
<jsp:root ...>
  <ns:customInclude src="inc/include.jspx" />
</jsp:root>

<!-- webapp.jar:/jspx/inc/include.jspx -->
<jsp:root ...>
  Default-Markup from the webapp.jar
</jsp:root>

<!-- /data/cust1/jspx/inc/include.jspx -->
<jsp:root ...>
  Custom markup for "cust1"
</jsp:root>

现在,当http://.../cust1/jspx/view.jspx被请求时,我想/data/cust1/jspx/inc/include.jspx由 Tomcat 编译和执行。基本上我知道实际上一切必要的东西在 Tomcat 中的某个地方已经是可能的(从 jspx 编译到 Java 字节码,包括文件,...),但我也知道 Tomcat/Jasper 遵守 jsp-spec。我通过查看代码发现这并不容易......

所以基本上,有谁知道如何让这样的设置工作?也许有人已经解决了这个问题?或者我目前的方法有其他选择吗?

4

1 回答 1

1

我最终实现了我自己的FileDirContext,它通过<Resources>-Tag in包含在context.xml. 实际上,我实现了两个 - 一个扩展org.apache.naming.resources.WARDirContext和一个扩展,org.apache.naming.resources.FileDirContext因为我想在使用 eclipse(部署到文件结构)和从非爆炸 .war 文件部署的服务器上启用相同的机制。

它们都可以通过上下文描述符进行配置,并且可以使用多个“虚拟覆盖”。覆盖可以是文件系统路径或外部 .war 文件(实际上更像是重命名为 .war 的 .zip)或打包到部署的 .war 文件中的 .jar 文件。

使用一个虚拟覆盖的调优解决方案的步骤是:

  1. 创建一个MagicWARContext从上述扩展而来的类(魔术就像解决每个问题;)WARDirContext
  2. 在这个类中创建一个静态内部类ProtectedMethodVisibleMaker extends FileDirContext。这允许访问 Tomcat 原始实现的某些受保护方法。覆盖doGetRealPathdoLookup
  3. 在您自己的 WAR 上下文中,为字段private String overlay和字段创建 getter 和 setter,并为该字段private ProtectedMethodVisibleMaker overlayContext实例化一个新的空实例。
  4. allocate()您的 WAR 上下文调用overlayContext.setDocBase(overlay)中。一定要打电话overlayContext.release()release()确定。
  5. 现在你必须覆盖另外 5 个方法WARDirContext
    • doGetRealPath(String)
    • doLookup(String)
    • getAttributes(Name, String[])
    • list(String)
    • list(Name) 两个 s 的最终枚举list当然应该包含虚拟覆盖的条目以及原始上下文。所以我最终编写了我自己NamingEnumeration的迭代一个委托命名枚举列表,并确保每个命名条目只输出一次。
  6. 从上下文描述符中实例化你的魔法上下文:

    <Resources 
        className="package.name.tomcatextensions.MagicWARContext" 
        overlay="/data/some/dir"/>
    
  7. 将您的课程打包在一个罐子中并将其复制到tomcat/lib

注意事项

我们正在覆盖 Tomcat 的内部类。这自然会将此代码暴露给更改。因此,必须有人确保版本升级后一切都按预期工作。而不是一些受保护的方法可以覆盖实际的公共方法,但其中一些是最终的,所以你基本上不走运。

此外,还可以通过查找方法向其他应用程序层公开非魔法子上下文,这意味着它们不再具有该覆盖或不包含已部署战争的后备资源。当请求子上下文时,我最终在日志中添加了一些警告。在启动过程中,这发生了两次,一次 for/WEB-INF/classes和一次 for /WEB-INF/lib
在我的用例中,我禁止添加包含其中任一路径的虚拟覆盖,因为我不想让外部类文件出现在我的 Web 上下文中,所以这不是问题。然而,其他一些客户可能正在调用这些方法并用它们做一些意想不到的事情。在我的测试过程中我没有发现任何问题,但是启用缓存或枚举资源时可能会出现奇怪的效果,并且会有使用子上下文时会出现问题...当然可以围绕查找返回的子上下文创建虚拟化包装器,但我还没有这样做,因为我希望这是不必要的。

当然,这种方法使您能够在非爆炸式战争部署中覆盖后备 webapp 的某些资源。如果您只需要将外部资源添加到 Web 上下文,您可以使用 Tomcat aliases(请参阅如何在 java 中将别名添加到 Servlet 上下文?)。

使用风险自负,玩得开心。

于 2013-04-09T07:26:40.857 回答