我可以从 lib 中的 JAR 内部提供 JSP,还是有解决方法?

2022-08-31 15:24:47

我有一个Web应用程序在Tomcat 7中部署为WAR文件。应用程序作为多模块项目生成:

  • core - 打包为JAR,包含大部分后端代码
  • core-api - 打包为 JAR,包含指向核心的接口
  • webapp - 打包为 WAR,包含前端代码并依赖于核心
  • 客户扩展 - 可选模块,打包为 JAR

通常,我们可以将 JSP 文件放在 webapp 项目中,并相对于上下文引用它们:

/WEB-INF/jsp/someMagicalPage.jsp

问题是我们对特定于客户扩展项目的 JSP 文件做了什么,这些文件不应始终包含在 WAR 中。不幸的是,我无法在JAR文件中引用JSP,它似乎出现了。尝试导致在 JspServlet 中找不到文件,因为它使用 .classpath:jsp/customerMagicalPage.jspServletContext.getResource()

传统上,我们通过解压缩客户扩展JAR,找到JSP,并在构建时将它们放入WAR中来“解决”这个问题。但一个理想的情况是,你只需在Tomcat的爆炸式WAR中放置一个JAR,就会发现扩展 - 这适用于除JSP之外的所有内容。

有没有办法解决这个问题?标准方式,Tomcat特定方式,黑客攻击还是解决方法?例如,我一直在考虑在应用程序启动时解压缩JSP...


答案 1

Tomcat 7 支持的 Servlet 3.0 包括将 jsps 打包到 jar 中的能力。

您需要:

  • 将你的jsps放在你的jar的目录中META-INF/resources
  • (可选)在 jar 的目录中包含web-fragment.xmlMETA-INF
  • 将罐子放在战争的目录中WEB-INF/lib

然后,您应该能够在上下文中引用您的jsps。例如,如果你有一个jsp,你应该能够在上下文的根部引用它作为META-INF/resources/test.jsptest.jsp


答案 2

作为一种解决方法,我创建了一个类,用于打开一个 jar 文件,查找与特定模式匹配的文件,并将这些文件提取到相对于上下文路径的给定位置。

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Enumeration;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

import javax.annotation.PostConstruct;
import javax.servlet.ServletContext;

import org.springframework.util.AntPathMatcher;
import org.springframework.web.context.ServletContextAware;

/**
 * Allows extraction of contents of a JAR file. All files matching a given Ant path pattern will be extracted into a
 * specified path.
 */
public class JarFileResourcesExtractor implements ServletContextAware {

    private String resourcePathPattern;
    private String jarFile;
    private String destination;
    private ServletContext servletContext;
    private AntPathMatcher pathMatcher = new AntPathMatcher();

    /**
     * Creates a new instance of the JarFileResourcesExtractor
     * 
     * @param resourcePathPattern
     *            The Ant style path pattern (supports wildcards) of the resources files to extract
     * @param jarFile
     *            The jar file (located inside WEB-INF/lib) to search for resources
     * @param destination
     *            Target folder of the extracted resources. Relative to the context.
     */
    private JarFileResourcesExtractor(String resourcePathPattern, String jarFile, String destination) {
        this.resourcePathPattern = resourcePathPattern;
        this.jarFile = jarFile;
        this.destination = destination;
    }

    /** 
     * Extracts the resource files found in the specified jar file into the destination path
     * 
     * @throws IOException
     *             If an IO error occurs when reading the jar file
     * @throws FileNotFoundException
     *             If the jar file cannot be found
     */
    @PostConstruct
    public void extractFiles() throws IOException {
        try {
            String path = servletContext.getRealPath("/WEB-INF/lib/" + jarFile);
            JarFile jarFile = new JarFile(path);

            Enumeration<JarEntry> entries = jarFile.entries();
            while (entries.hasMoreElements()) {
                JarEntry entry = entries.nextElement();
                if (pathMatcher.match(resourcePathPattern, entry.getName())) {
                    String fileName = entry.getName().replaceFirst(".*\\/", "");
                    File destinationFolder = new File(servletContext.getRealPath(destination));
                    InputStream inputStream = jarFile.getInputStream(entry);
                    File materializedJsp = new File(destinationFolder, fileName);
                    FileOutputStream outputStream = new FileOutputStream(materializedJsp);
                    copyAndClose(inputStream, outputStream);
                }
            }

        }
        catch (MalformedURLException e) {
            throw new FileNotFoundException("Cannot find jar file in libs: " + jarFile);
        }
        catch (IOException e) {
            throw new IOException("IOException while moving resources.", e);
        }
    }

    @Override
    public void setServletContext(ServletContext servletContext) {
        this.servletContext = servletContext;
    }

    public static int IO_BUFFER_SIZE = 8192;

    private static void copyAndClose(InputStream in, OutputStream out) throws IOException {
        try {
            byte[] b = new byte[IO_BUFFER_SIZE];
            int read;
            while ((read = in.read(b)) != -1) {
                out.write(b, 0, read);
            }
        } finally {
            in.close();
            out.close();
        }
    }
}

然后我在我的Spring XML中将其配置为bean:

<bean id="jspSupport" class="se.waxwing.util.JarFileResourcesExtractor">
   <constructor-arg index="0" value="jsp/*.jsp"/>
   <constructor-arg index="1" value="myJarFile-1.1.0.jar"/>
   <constructor-arg index="2" value="WEB-INF/classes/jsp"/>
</bean>

这不是一个非常烦人的问题的最佳解决方案。现在的问题是,维护这个代码的人会来谋杀我,而我睡觉时这样做?