Can I serve the JSP from JAR to lib, or is there a workaround?

I have a web application deployed as a WAR file in Tomcat 7. The application is built as a multi-module project:

  • Core
  • packaged as a JAR contains most of the backend code
  • core-api - packaged as a JAR, contains interfaces to the kernel
  • webapp - packaged as WAR, contains interface code and depends on the kernel
  • client extensions - add-on packaged as a JAR

Usually we can put our JSP files in a webapp project and reference them relative to context:

/WEB-INF/jsp/someMagicalPage.jsp 

The question is what we are doing with JSP files specific to the client extension project, which should not always be included in the WAR. Unfortunately, I cannot reference JSP inside JAR files, it seems. Trying classpath:jsp/customerMagicalPage.jsp results in the file not being found in JspServlet, as it uses ServletContext.getResource() .

Traditionally, we β€œsolve” this when maven unpacks the client extension JARs, hosts the JSP, and puts them in the WAR when it is created. But the ideal situation is that you simply throw the JAR into the exploded WAR in Tomcat and discover an extension - which works for everything except the JSP.

Is there any way to solve this? Standard way, Tomcat way to hack or workaround? For example, I was thinking about unpacking the JSP when the application started ...

+61
java jsp tomcat servlets tiles
Feb 16 2018-11-11T00:
source share
5 answers

Servlet 3.0, supported by Tomcat 7, includes the ability to pack jsps into a jar.

You need:

  • put your jsps in the META-INF/resources your jar
  • optional to include web-fragment.xml in the META-INF your banner
  • put the jar in the WEB-INF/lib your war

You can then reference your jsps in your context. For example, if you have jsp META-INF/resources/test.jsp , you should be able to reference this in the root of your context as test.jsp

+65
Jun 16 '12 at 11:34
source share

As a workaround, I created a class that opens a jar file, finds files matching a specific pattern, and extracts these files to a given location relative to the context path.

 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(); } } } 

And then I configure it as a bean in my 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> 

This is not an optimal solution to a really annoying problem. The question now becomes, will there be a guy who supports this code and kill me while I sleep for it?

+6
Feb 16 '11 at 13:00
source share

There is such a workaround - you can precompile your JSPs into servlets. This way you get .class files that you can put in the JAR and map to web.xml with some URLs.

+4
Feb 16 '11 at 8:21
source share

The Struts 2 team has added a plugin for the embedded JSP. Maybe this can be used as a base.

https://struts.apache.org/plugins/embedded-jsp/

+4
Apr 13 2018-11-11T00:
source share

This is the answer to the waxwing request that I used because we used a server that could not do anything higher than servlet 2.5.

I added a method that deletes files added when the bean is destroyed.

 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(); } } } 

Then I changed the constructor to use the whole java configuration:

 @Bean public JarFileResourcesExtractor jspSupport(){ final JarFileResourcesExtractor extractor = new JarFileResourcesExtractor("WEB-INF/pages/*.jsp","myJarFile-1.1.0.jar","WEB-INF/pages" ); return extractor; } 

I hope someone helps this to someone!

+1
May 17 '16 at 15:06
source share



All Articles