J'ai une application Web déployée en tant que fichier WAR dans Tomcat 7. L'application est conçue comme un projet multi-modules:
Normalement, nous pouvons mettre nos fichiers JSP dans le projet webapp, et les référencer par rapport au contexte:
/WEB-INF/jsp/someMagicalPage.jsp
La question est de savoir ce que nous faisons des fichiers JSP spécifiques au projet d'extensions client, qui ne doivent pas toujours être inclus dans le WAR. Malheureusement, je ne peux pas me référer aux JSP dans les fichiers JAR, il apparaît. La tentative de classpath:jsp/customerMagicalPage.jsp
Entraîne un fichier introuvable dans le JspServlet, car il utilise ServletContext.getResource()
.
Traditionnellement, nous "résolvions" ce problème en maven déballant le JAR des extensions client, localisant les JSP et les plaçant dans le WAR lors de sa construction. Mais une situation idéale est où vous déposez simplement un JAR dans la guerre éclatée de Tomcat et l'extension est découverte - qui fonctionne pour tout sauf les JSP.
Est-ce qu'il y a un moyen de résoudre ceci? Une méthode standard, une méthode spécifique à Tomcat, un hack ou une solution de contournement? Par exemple, j'ai pensé à déballer les JSP au démarrage de l'application ...
Servlet 3.0 pris en charge par Tomcat 7 inclut la possibilité de regrouper jsps dans un bocal.
Tu dois:
META-INF/resources
répertoire de votre potweb-fragment.xml
dans le META-INF
répertoire de votre potWEB-INF/lib
répertoire de votre guerreVous devriez alors pouvoir référencer vos jsps dans votre contexte. Par exemple, si vous avez un jsp META-INF/resources/test.jsp
vous devriez pouvoir référencer ceci à la racine de votre contexte comme test.jsp
Pour contourner ce problème, j'ai créé une classe qui ouvre un fichier jar, recherche les fichiers correspondant à un certain modèle et extrait ces fichiers à un emplacement donné par rapport au chemin du contexte.
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();
}
}
}
Et puis je le configure comme un bean dans mon XML Spring:
<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>
Ce n'est pas une solution optimale à un problème vraiment ennuyeux. La question devient maintenant, est-ce que le gars qui maintient ce code viendra et assassinez-moi pendant que je dors pour ça?
Il existe une telle solution - vous pouvez précompiler vos JSP en servlets. Ainsi, vous obtiendrez des fichiers .class que vous pouvez placer dans JAR et mapper dans web.xml à certaines URL.
L'équipe Struts 2 a ajouté un plugin pour JSP intégré. Peut-être qu'il peut être utilisé sur une base.
Ceci est une réponse à waxwing, que j'ai utilisée car nous avons utilisé un serveur qui ne pouvait rien faire de plus que le servlet 2.5.
J'ai ajouté une méthode qui supprime les fichiers ajoutés lorsque le bean est détruit.
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();
}
}
}
Ensuite, j'ai changé le constructeur pour pouvoir utiliser toutes les configurations Java:
@Bean
public JarFileResourcesExtractor jspSupport(){
final JarFileResourcesExtractor extractor = new JarFileResourcesExtractor("WEB-INF/pages/*.jsp","myJarFile-1.1.0.jar","WEB-INF/pages" );
return extractor;
}
J'espère que quelqu'un aide quelqu'un!