我在 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 来“解决”这个问题。但是理想的情况是,您只需在 Tomcat 中的爆炸 WAR 中放置一个 JAR 并发现扩展 - 它适用于除 JSP 之外的所有内容。
有没有办法解决这个问题?标准方式、Tomcat 特定方式、hack 还是解决方法?例如,我一直在考虑在应用程序启动时解压缩 JSP...
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
作为一种解决方法,我创建了一个类,它打开一个 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?
有这样的解决方法 - 您可以将 JSP 预编译成 servlet。因此,您将获得 .class 文件,您可以将其放入 JAR 中并在 web.xml 中映射到某些 URL。
这是waxwing answer的后续,我使用了它,因为我们使用的服务器无法执行高于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;
}
我希望有人能帮助别人!