Czy mogę podawać JSP z wnętrza słoika w lib, czy istnieje obejście?

Mam aplikację internetową wdrożoną jako plik wojenny w Tomcat 7. Aplikacja jest budowana jako projekt wielomodułowy:

  • core-pakowany jako JAR, zawiera większość kodu backendu
  • core-api-pakowane jako JAR, zawiera interfejsy do core
  • webapp-pakowany jako WAR, zawiera kod frontend i zależy od rdzenia
  • customer-extensions-opcjonalny moduł, pakowany jako JAR

Normalnie możemy umieścić nasze pliki JSP w projekcie webapp, a odwołaj się do nich względem kontekstu:

/WEB-INF/jsp/someMagicalPage.jsp

Pytanie brzmi, co robimy z plikami JSP, które są specyficzne dla projektu customer-extensions, które nie zawsze powinny być uwzględnione w wojnie. Niestety, nie mogę odnieść się do JSP wewnątrz plików JAR, wygląda na to. Próba classpath:jsp/customerMagicalPage.jsp spowoduje, że plik nie zostanie znaleziony w JspServlet, ponieważ używa ServletContext.getResource().

Tradycyjnie "rozwiązaliśmy" to po rozpakowaniu przez Mavena słoika rozszerzeń klienta, zlokalizowaniu JSP i umieszczeniu ich w wojnie, gdy buduję go. Ale idealną sytuacją jest to, że po prostu upuszczasz słoik w wybuchu wojny w Tomcat i odkrywa się rozszerzenie - które działa na wszystko, oprócz JSP.

Czy można to jakoś rozwiązać? Standardowy sposób, specyficzny dla Tomcata sposób, hack, czy obejście? Na przykład myślałem o rozpakowaniu JSP podczas uruchamiania aplikacji...
Author: waxwing, 2011-02-16

5 answers

Servlet 3.0, który obsługuje Tomcat 7, zawiera możliwość spakowania JSP do jar.

Musisz:

  • umieść swoje JSP w META-INF/resources katalogu swojego jar
  • opcjonalnie dołącz web-fragment.xml do katalogu META-INF swojego jar
  • umieść słoik w WEB-INF/lib katalogu swojej wojny

Powinieneś być w stanie odwołać się do swoich JSP w swoim kontekście. Na przykład, jeśli masz jsp META-INF/resources/test.jsp powinieneś być w stanie odwołać się do tego w źródle kontekstu jako test.jsp

 64
Author: scarba05,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-06-16 11:34:22

Jako obejście stworzyłem klasę, która otwiera plik jar, wyszukuje pliki pasujące do określonego wzorca i wyodrębnia te pliki do określonej lokalizacji względem ścieżki kontekstowej.

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

A potem konfiguruję go jako fasolkę w moim 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>
To nie jest optymalne rozwiązanie naprawdę irytującego problemu. Teraz pojawia się pytanie, czy facet, który utrzymuje ten kod, przyjdzie i zamorduje mnie, gdy śpię za zrobienie tego?
 6
Author: waxwing,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-02-16 15:09:27

Istnieje takie obejście-możesz wstępnie skompilować swoje JSP do servletów. Więc dostaniesz .pliki klas można umieścić w JAR i map w web.xml do niektórych adresów URL.

 4
Author: Paul Lysak,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-02-16 08:21:31

Zespół Struts 2 dodał wtyczkę do wbudowanego JSP. Może może być używany jako baza.

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

 4
Author: apetrelli,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-04-13 14:41:11

To jest follup do waxwing odpowiedź, której użyłem ponieważ użyliśmy serwera, który nie mógł zrobić nic wyższego niż servlet 2.5.

Dodałem metodę, która usuwa pliki dodane po zniszczeniu fasoli.

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

Potem zmieniłem konstruktor, aby móc korzystać z całej konfiguracji Javy:

@Bean 
public JarFileResourcesExtractor jspSupport(){
    final JarFileResourcesExtractor extractor = new JarFileResourcesExtractor("WEB-INF/pages/*.jsp","myJarFile-1.1.0.jar","WEB-INF/pages" );
    return extractor;
}
Mam nadzieję, że komuś to pomoże!
 1
Author: Björn Bergenheim,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-05-17 15:06:37