首页 文章

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

提问于
浏览
59

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

  • core - 打包为JAR,包含大部分后端代码

  • core-api - 打包为JAR,包含对核心的接口

  • webapp - 打包为WAR,包含前端代码并依赖于核心

  • customer-extensions - 可选模块,打包为JAR

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

/WEB-INF/jsp/someMagicalPage.jsp

问题是我们如何处理特定于客户扩展项目的JSP文件,这些文件不应始终包含在WAR中 . 不幸的是,我看不到JAR文件中的JSP . 尝试 classpath:jsp/customerMagicalPage.jsp 导致在JspServlet中找不到文件,因为它使用 ServletContext.getResource() .

传统上,我们“解决”了maven解包客户扩展JAR,定位JSP,并在构建它时将它们放入WAR中 . 但理想的情况是,您只需在Tomcat中的爆炸WAR中放置JAR并发现扩展 - 这适用于除JSP之外的所有内容 .

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

5 回答

  • 4

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

    你需要:

    • 将你的jsps放在jar的 META-INF/resources 目录中

    • 可选地在jar的 META-INF 目录中包含 web-fragment.xml

    • 将jar放在战争的 WEB-INF/lib 目录中

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

  • 64

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

    它不是一个真正恼人的问题的最佳解决方案 . 现在的问题是,维护此代码的人会来吗?murder me while I sleep for doing this?

  • 1

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

  • 4

    Struts 2团队为嵌入式JSP添加了一个插件 . 也许它可以用于基地 .

    http://struts.apache.org/2.x/docs/embedded-jsp-plugin.html

  • 6

    这是一个很好的回答,我已经使用了,因为我们使用的服务器无法做任何高于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;
    }
    

    我希望有人帮这个人!

相关问题