Могу ли я обслуживать JSP из JAR в lib, или есть обходной путь?
У меня есть веб-приложение, развернутое как WAR файл в Tomcat 7. Приложение построено как многомодульный проект:
-
Ядро
- упакованное как JAR, содержит большую часть бэкэнд-кода
- core-api - упакован как JAR, содержит интерфейсы к ядру
- webapp - упакован как WAR, содержит код интерфейса и зависит от ядра
- клиентские расширения - дополнительный модуль, упакованный как JAR
Обычно мы можем помещать наши JSP файлы в проект webapp и ссылаться на них относительно контекста:
/WEB-INF/jsp/someMagicalPage.jsp
Вопрос в том, что мы делаем с JSP файлами, специфичными для проекта расширения клиентов, который не всегда должен быть включен в WAR. К сожалению, я не могу ссылаться на JSP внутри JAR файлов, похоже. Попытка classpath:jsp/customerMagicalPage.jsp
приводит к тому, что файл не найден в JspServlet, так как он использует ServletContext.getResource()
.
Традиционно мы "решаем" это, когда maven распаковывает JAR клиента-расширений, размещает JSP и помещает их в WAR при ее создании. Но идеальной ситуацией является то, что вы просто бросаете JAR в взорванную WAR в Tomcat и обнаруживаете расширение - которое работает для всего, кроме JSP.
Есть ли способ решить это? Стандартный способ, способ Tomcat, взломать или обходной путь? Например, я думал о распаковке JSP при запуске приложения...
Ответы
Ответ 1
Servlet 3.0, поддерживаемый Tomcat 7, включает возможность упаковки jsps в банку.
Вам необходимо:
- поместите ваш jsps в каталог
META-INF/resources
вашего банку
- необязательно включать
web-fragment.xml
в каталог META-INF
вашего баннера
- поместите банку в каталог
WEB-INF/lib
вашей войны
Затем вы можете ссылаться на свои jsps в своем контексте. Например, если у вас есть jsp META-INF/resources/test.jsp
, вы должны иметь возможность ссылаться на это в корне вашего контекста как test.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();
}
}
}
И затем я настраиваю его как bean в моем 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>
Это не оптимальное решение действительно раздражающей проблемы. Вопрос теперь становится, будет ли парень, который поддерживает этот код, и убить меня, пока я сплю за это?
Ответ 3
Существует такое обходное решение - вы можете предварительно скомпилировать свои JSP в сервлеты. Таким образом, вы получите файлы .class, которые вы можете поместить в JAR и сопоставить в web.xml с некоторыми URL-адресами.
Ответ 4
Команда Struts 2 добавила плагин для встроенного JSP. Может быть, это может быть использовано в качестве базы.
https://struts.apache.org/plugins/embedded-jsp/
Ответ 5
Это ответ на запрос waxwing, который я использовал, потому что мы использовали сервер, который не мог сделать ничего выше, чем 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/info/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;
}
Я надеюсь, что кто-то это поможет кому-то!