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

发布于 2024-10-17 09:10:49 字数 726 浏览 2 评论 0原文

我在 Tomcat 7 中部署了一个 Web 应用程序作为 WAR 文件。该应用程序构建为多模块项目:

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

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

/WEB-INF/jsp/someMagicalPage.jsp

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

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

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

I have a web application deployed as a WAR file in Tomcat 7. The application is build as a multi-module project:

  • core - packaged as JAR, contains most of the backend code
  • core-api - packaged as JAR, contains interfaces toward core
  • webapp - packaged as WAR, contains frontend code and depends on core
  • customer-extensions - optional module, packaged as JAR

Normally, we can put our JSP files in the webapp project, and reference them relative to the context:

/WEB-INF/jsp/someMagicalPage.jsp

The question is what we do about JSP files that are specific to the customer-extensions project, that should not always be included in the WAR. Unfortunately, I cannot refer to JSPs inside JAR files, it appears. Attempting classpath:jsp/customerMagicalPage.jsp results in a file not found in the JspServlet, since it uses ServletContext.getResource().

Traditionally, we "solved" this having maven unpack the customer-extensions JAR, locate the JSPs, and put them in the WAR when building it. But an ideal situation is where you just drop a JAR in the exploded WAR in Tomcat and the extension is discovered - which works for everything but the JSPs.

Is there anyway to solve this? A standard way, a Tomcat-specific way, a hack, or a workaround? For example, I've been thinking of unpacking the JSPs on application startup...

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

自由如风 2024-10-24 09:10:49

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

您需要:

  • 将 jsps 放置在 jar 的 META-INF/resources 目录中
  • (可选)在 META-INFweb-fragment.xml > jar 的目录
  • 将 jar 放在 war 的 WEB-INF/lib 目录中,

然后您应该能够在上下文中引用您的 jsps。例如,如果您有一个 jsp META-INF/resources/test.jsp,您应该能够在上下文的根目录中将其引用为 test.jsp

Servlet 3.0 which Tomcat 7 supports includes the ability to package jsps into a jar.

You need to:

  • place your jsps in META-INF/resources directory of your jar
  • optionally include a web-fragment.xml in the META-INF directory of your jar
  • place the jar in WEB-INF/lib directory of your war

You should then be able to reference your jsps in your context. For example if you have a jsp META-INF/resources/test.jsp you should be able reference this at the root of your context as test.jsp

终难遇 2024-10-24 09:10:49

作为解决方法,我创建了一个类,该类打开一个 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>

这不是解决真正烦人问题的最佳解决方案。现在的问题是,维护此代码的人会来因为这样做而在我睡觉时谋杀我吗?

As a workaround, I created a class that opens up a jar file, finds files matching a certain pattern, and extracts those files to a given location relative to the context path.

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();
        }
    }
}

And then I configure it as a bean in my Spring XML:

<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>

It's not an optimal solution to a really annoying problem. The question now becomes, will the guy who maintains this code come and murder me while I sleep for doing this?

冧九 2024-10-24 09:10:49

有这样的解决方法 - 您可以将 JSP 预编译为 servlet。因此,您将获得 .class 文件,您可以将其放入 JAR 中并在 web.xml 中映射到某些 URL。

There is such workaround - you can precompile your JSPs into servlets. So you'll get .class files you can put into JAR and map in web.xml to some URLs.

野味少女 2024-10-24 09:10:49

Struts 2 团队添加了一个用于嵌入式 JSP 的插件。也许它可以用作基础。

https://struts.apache.org/plugins/embedded-jsp/

Struts 2 team added a plugin for embedded JSP. Maybe it may be used ad a base.

https://struts.apache.org/plugins/embedded-jsp/

陈甜 2024-10-24 09:10:49

这是蜡翼答案的后续内容,我使用它是因为我们使用的服务器无法执行高于 servlet 2.5 的任何操作。

我添加了一个方法,可以删除 bean 被销毁时添加的文件。

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.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

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

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


import com.sap.tc.logging.Location;

/**
 * Allows extraction of contents of a JAR file. All files matching a given Ant path pattern will be extracted into a
 * specified path.
 * Copied from http://stackoverflow.com/questions/5013917/can-i-serve-jsps-from-inside-a-jar-in-lib-or-is-there-a-workaround
 */
public class JarFileResourcesExtractor implements ServletContextAware {

    private final transient Location logger = Location.getLocation(JarFileResourcesExtractor.class);

    private String resourcePathPattern;
    private String jarFile;
    private String destination;
    private ServletContext servletContext;
    private AntPathMatcher pathMatcher = new AntPathMatcher();
    private List<File> listOfCopiedFiles = new ArrayList<File>();

    /**
     * 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.
     */
    public JarFileResourcesExtractor(String resourcePathPattern, String jarFile, String destination) {
        this.resourcePathPattern = resourcePathPattern;
        this.jarFile = jarFile;
        this.destination = destination;
    }


    @PreDestroy
    public void removeAddedFiles() throws IOException{
        logger.debugT("I removeAddedFiles()");
        for (File fileToRemove : listOfCopiedFiles) {
            if(fileToRemove.delete()){
                logger.debugT("Tagit bort filen " + fileToRemove.getAbsolutePath());
            }
        }
    }


    /** 
     * 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);
                    listOfCopiedFiles.add(materializedJsp);
                    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();
        }
    }
}

然后我确实更改了构造函数,以便我可以使用所有 java 配置:

@Bean 
public JarFileResourcesExtractor jspSupport(){
    final JarFileResourcesExtractor extractor = new JarFileResourcesExtractor("WEB-INF/pages/*.jsp","myJarFile-1.1.0.jar","WEB-INF/pages" );
    return extractor;
}

我希望这可以帮助某人!

This is a follup to waxwing answer, which I've used becuase we used a server that could not do anything higher then servlet 2.5.

I added a method that removes the files added when the bean is destroyed.

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.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

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

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


import com.sap.tc.logging.Location;

/**
 * Allows extraction of contents of a JAR file. All files matching a given Ant path pattern will be extracted into a
 * specified path.
 * Copied from http://stackoverflow.com/questions/5013917/can-i-serve-jsps-from-inside-a-jar-in-lib-or-is-there-a-workaround
 */
public class JarFileResourcesExtractor implements ServletContextAware {

    private final transient Location logger = Location.getLocation(JarFileResourcesExtractor.class);

    private String resourcePathPattern;
    private String jarFile;
    private String destination;
    private ServletContext servletContext;
    private AntPathMatcher pathMatcher = new AntPathMatcher();
    private List<File> listOfCopiedFiles = new ArrayList<File>();

    /**
     * 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.
     */
    public JarFileResourcesExtractor(String resourcePathPattern, String jarFile, String destination) {
        this.resourcePathPattern = resourcePathPattern;
        this.jarFile = jarFile;
        this.destination = destination;
    }


    @PreDestroy
    public void removeAddedFiles() throws IOException{
        logger.debugT("I removeAddedFiles()");
        for (File fileToRemove : listOfCopiedFiles) {
            if(fileToRemove.delete()){
                logger.debugT("Tagit bort filen " + fileToRemove.getAbsolutePath());
            }
        }
    }


    /** 
     * 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);
                    listOfCopiedFiles.add(materializedJsp);
                    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();
        }
    }
}

Then I did change the constructor so I could use all java configuration:

@Bean 
public JarFileResourcesExtractor jspSupport(){
    final JarFileResourcesExtractor extractor = new JarFileResourcesExtractor("WEB-INF/pages/*.jsp","myJarFile-1.1.0.jar","WEB-INF/pages" );
    return extractor;
}

I hope someone this helps someone!

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文