java - ejemplos - ¿Puedo servir JSP desde dentro de un JAR en lib, o hay una solución alternativa?
jstl maven (5)
Tengo una aplicación web implementada como un archivo WAR en Tomcat 7. La aplicación se compila como un proyecto de varios módulos:
- core - empaquetado como JAR, contiene la mayor parte del código de back-end
- core-api - empaquetado como JAR, contiene interfaces hacia el núcleo
- webapp: empaquetado como WAR, contiene código de interfaz y depende del núcleo
- extensiones de cliente - módulo opcional, empaquetado como JAR
Normalmente, podemos poner nuestros archivos JSP en el proyecto de webapp y hacer referencia a ellos en relación con el contexto:
/WEB-INF/jsp/someMagicalPage.jsp
La pregunta es qué hacemos con los archivos JSP que son específicos del proyecto de extensiones de clientes, que no siempre deberían incluirse en el WAR. Desafortunadamente, no puedo referirme a las JSP dentro de los archivos JAR, parece. Al intentar classpath:jsp/customerMagicalPage.jsp
genera un archivo que no se encuentra en JspServlet, ya que usa ServletContext.getResource()
.
Tradicionalmente, hemos "resuelto" esto teniendo maven desempaquetar el archivo JAR de extensiones de cliente, ubicar los JSP y ponerlos en el WAR al construirlo. Pero una situación ideal es cuando solo lanzas un JAR en el WAR explosionado en Tomcat y se descubre la extensión, que funciona para todo menos para los JSP.
¿Hay alguna forma de resolver esto? ¿Una forma estándar, una forma específica de Tomcat, un truco o una solución alternativa? Por ejemplo, he estado pensando en desempaquetar los JSP en el inicio de la aplicación ...
Como solución alternativa, creé una clase que abre un archivo jar, busca archivos que coincidan con un patrón determinado y extrae esos archivos en una ubicación determinada en relación con la ruta de contexto.
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();
}
}
}
Y luego lo configuro como un bean en mi 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>
No es una solución óptima para un problema realmente molesto. La pregunta ahora es, ¿vendrá el tipo que mantiene este código y me asesinará mientras duermo por hacer esto?
El equipo de Struts 2 agregó un complemento para JSP integrado. Tal vez se pueda usar ad a base.
Esta es una respuesta similar a la de waxwing, que he usado porque utilizamos un servidor que no podía hacer nada más alto que el servlet 2.5.
Agregué un método que elimina los archivos agregados cuando se destruye el 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://.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();
}
}
}
Luego cambié el constructor para poder usar toda la configuración de Java:
@Bean
public JarFileResourcesExtractor jspSupport(){
final JarFileResourcesExtractor extractor = new JarFileResourcesExtractor("WEB-INF/pages/*.jsp","myJarFile-1.1.0.jar","WEB-INF/pages" );
return extractor;
}
Espero que alguien esto ayude a alguien!
Existe tal solución: puede precompilar sus JSP en servlets. Por lo tanto, obtendrá archivos .class que puede colocar en JAR y asignar en web.xml a algunas URL.
Servlet 3.0 que admite Tomcat 7 incluye la capacidad de empaquetar jsps en un contenedor.
Necesitas:
- coloca tu jsps en el directorio
META-INF/resources
de tu jar - opcionalmente incluya un
web-fragment.xml
en el directorioMETA-INF
de su jar - coloca el contenedor en el directorio
WEB-INF/lib
de tu guerra
Debería poder hacer referencia a su jsps en su contexto. Por ejemplo, si tiene un jsp META-INF/resources/test.jsp
, debería poder hacer referencia a esto en la raíz de su contexto como test.jsp