我可以在JAR中使用JSP来提供JSP,还是有解决方法?

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

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

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

/WEB-INF/jsp/someMagicalPage.jsp 

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

传统上,我们通过maven解开客户扩展JAR,findJSP,并在构build它时将它们放入WAR。 但是理想的情况是,你只需要在Tomcat的WAR文件中放入一个JAR文件,就可以发现这个扩展文件 – 除了JSP之外,其他的文件都可以使用。

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

Tomcat 7支持的Servlet 3.0包括将jsps打包成jar的function。

你需要:

  • 把你的jsps放在jar的META-INF/resources目录下
  • 可以在jar的META-INF目录中包含一个web-fragment.xml文件
  • 把jar放在你的war的WEB-INF/lib目录下

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

作为一种解决方法,我创build了一个打开jar文件的类,find匹配某个模式的文件,并将这些文件提取到相对于上下文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(); } } } 

然后在Spring XML中将其configuration为一个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> 

这不是一个真正烦人的问题的最佳解决scheme。 现在的问题是,维持这个代码的人是否会在我睡觉的时候杀了我?

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

Struts 2团队为embedded式JSP添加了一个插件。 也许它可能被用于广告基地。

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

这是一个蜡烛答案,我曾经使用过,因为我们使用了一个服务器,无法做任何比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(); } } } 

然后我改变了构造函数,所以我可以使用所有的Javaconfiguration:

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

我希望有人帮助别人!