Puis-je servir des JSP depuis un JAR dans lib, ou existe-t-il une solution de contournement?

J’ai une application Web déployée en tant que fichier WAR dans Tomcat 7. L’application est créée en tant que projet multi-module:

  • core – emballé en JAR, contient la plupart du code backend
  • core-api – packagé en JAR, contient des interfaces vers le core
  • webapp – emballé sous forme de WAR, contient du code frontal et dépend du kernel
  • extensions client – module optionnel, emballé en JAR

Normalement, nous pouvons placer 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 à propos des fichiers JSP spécifiques au projet d’extensions client, qui ne doivent pas toujours être inclus dans le fichier WAR. Malheureusement, je ne peux pas faire référence aux fichiers JSP dans les fichiers JAR, cela apparaît. Tentative de classpath:jsp/customerMagicalPage.jsp génère un fichier introuvable dans JspServlet, car il utilise ServletContext.getResource() .

Traditionnellement, nous “résolvions” le fait de devoir décompresser le fichier JAR des extensions client, localiser les fichiers JSP et les placer dans le fichier WAR lors de sa construction. Mais une situation idéale est de déposer un JAR dans le WAR explosé dans Tomcat et que l’extension soit découverte – ce qui fonctionne pour tout sauf les JSP.

Est-ce qu’il y a un moyen de résoudre ceci? Une méthode standard, spécifique à Tomcat, un hack ou une solution de contournement? Par exemple, j’ai pensé à décompresser les JSP au démarrage de l’application …

    Servlet 3.0 pris en charge par Tomcat 7 inclut la possibilité d’emballer des jsps dans un jar.

    Tu dois:

    • placez vos jsps dans le META-INF/resources de votre pot
    • éventuellement, inclure un web-fragment.xml dans le META-INF de votre fichier jar
    • placez le pot dans le WEB-INF/lib de votre guerre

    Vous devriez alors pouvoir référencer vos jsps dans votre contexte. Par exemple, si vous avez un fichier jsp META-INF/resources/test.jsp vous devriez pouvoir le référencer à la racine de votre contexte en tant que 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 de 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 Ssortingng resourcePathPattern; private Ssortingng jarFile; private Ssortingng 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(Ssortingng resourcePathPattern, Ssortingng jarFile, Ssortingng 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 { Ssortingng path = servletContext.getRealPath("/WEB-INF/lib/" + jarFile); JarFile jarFile = new JarFile(path); Enumeration ensortinges = jarFile.ensortinges(); while (ensortinges.hasMoreElements()) { JarEntry entry = ensortinges.nextElement(); if (pathMatcher.match(resourcePathPattern, entry.getName())) { Ssortingng 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 en tant que bean dans mon XML de spring:

          

    Ce n’est pas une solution optimale à un problème vraiment ennuyeux. La question devient maintenant, le gars qui maintient ce code va-t-il venir me tuer pendant que je dors pour faire ça?

    Il existe une solution de contournement – vous pouvez précomstackr vos JSP en servlets. Vous obtiendrez donc des fichiers .class que vous pouvez placer dans JAR et mapper dans web.xml sur certaines URL.

    L’équipe Struts 2 a ajouté un plug-in pour JSP intégré. Peut-être que cela peut être utilisé comme base.

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

    Ceci est une réponse à la cire, que j’ai utilisée car nous avons utilisé un serveur qui ne pouvait rien faire de plus haut 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 Ssortingng resourcePathPattern; private Ssortingng jarFile; private Ssortingng destination; private ServletContext servletContext; private AntPathMatcher pathMatcher = new AntPathMatcher(); private List listOfCopiedFiles = new ArrayList(); /** * 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(Ssortingng resourcePathPattern, Ssortingng jarFile, Ssortingng 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 { Ssortingng path = servletContext.getRealPath("/WEB-INF/lib/" + jarFile); JarFile jarFile = new JarFile(path); Enumeration ensortinges = jarFile.ensortinges(); while (ensortinges.hasMoreElements()) { JarEntry entry = ensortinges.nextElement(); if (pathMatcher.match(resourcePathPattern, entry.getName())) { Ssortingng 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 toute la configuration 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!