Bug 60940

Summary: "unpackWARs=false" causes different behavior of classloader
Product: Tomcat 8 Reporter: vw_alucard
Component: CatalinaAssignee: Tomcat Developers Mailing List <dev>
Status: RESOLVED FIXED    
Severity: normal    
Priority: P2    
Version: 8.0.42   
Target Milestone: ----   
Hardware: PC   
OS: All   

Description vw_alucard 2017-03-30 05:34:08 UTC
Hi all,

when I try to run my JSF 2.2 application which uses Richfaces on a Tomcat 8 with "unpackWARs=false" some parts of Richfaces/JSF will not be loaded during startup. This is caused by a different behavior of the classloader when the app cannot be unpacked by the server. 

During JSF startup is searches for all Jar files which include a "META-INF/" folder. This is done by calling getResources("META-INF/") on the classloader. In all found resources it searches for taglibs and faces config extensions. The problem now is, that the calssloader returns a different number of resources when the app is unpacked and when the app is deployed a zip file. 

This is caused by a the way the classloader deals with JarWarRessources. When the app is unpacked the server uses direct jar file access und searches in the zip file for the folder. In this case every jar with a "META-INF/" folder is found. When the app is a war file which cannot be unpacked by the server the "JarWarRessourceSet" uses a "JarInputStream" to read and cache the jar files content. But this input stream skips the "META-INF/" folder by default:

JarFileInputStream.java

...

    public JarInputStream(InputStream in, boolean verify) throws IOException {
        super(in);
        this.doVerify = verify;

        // This implementation assumes the META-INF/MANIFEST.MF entry
        // should be either the first or the second entry (when preceded
        // by the dir META-INF/). It skips the META-INF/ and then
        // "consumes" the MANIFEST.MF to initialize the Manifest object.
        JarEntry e = (JarEntry)super.getNextEntry();
        if (e != null && e.getName().equalsIgnoreCase("META-INF/"))
            e = (JarEntry)super.getNextEntry();
        first = checkManifest(e);
    }

... 

This will cause the issues with the app because some parts(taglibs and faces config extensions) cannot be loaded. 

You can reproduce the issue with the richfaces sample project which can be generated via maven:

mvn archetype:generate -DarchetypeGroupId=org.richfaces.archetypes -DarchetypeArtifactId=richfaces-archetype-simpleapp -DarchetypeVersion=4.5.17.Final -DgroupId=org.docs.richfaces -DartifactId=new_project

Replace the sample projects pom by this one (sorry I'm not able to upload files from this workstation):
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

    <modelVersion>4.0.0</modelVersion>

    <groupId>org.docs.richfaces</groupId>
    <artifactId>new_project</artifactId>
    <name>RichFaces 4 Application</name>
    <version>1.0-SNAPSHOT</version>
    <packaging>war</packaging>

    <url>http://jboss.org/richfaces</url>

    <repositories>
        <!-- You should seriously consider using a repository manager or declare repositories in your settings.xml.
        See http://www.sonatype.com/people/2009/02/why-putting-repositories-in-your-poms-is-a-bad-idea/   -->
        <repository>
            <id>jboss-public-repository-group</id>
            <name>JBoss Public Maven Repository Group</name>
            <url>https://repository.jboss.org/nexus/content/groups/public-jboss/</url>
            <layout>default</layout>
            <releases>
                <enabled>true</enabled>
                <updatePolicy>never</updatePolicy>
            </releases>
            <snapshots>
                <enabled>true</enabled>
                <updatePolicy>never</updatePolicy>
            </snapshots>
        </repository>
    </repositories>
    <pluginRepositories>
        <pluginRepository>
            <id>jboss-public-repository-group</id>
            <name>JBoss Public Maven Repository Group</name>
            <url>https://repository.jboss.org/nexus/content/groups/public-jboss/</url>
            <layout>default</layout>
            <releases>
                <enabled>true</enabled>
                <updatePolicy>never</updatePolicy>
            </releases>
            <snapshots>
                <enabled>true</enabled>
                <updatePolicy>never</updatePolicy>
            </snapshots>
        </pluginRepository>
    </pluginRepositories>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.6</maven.compiler.source>
        <maven.compiler.target>1.6</maven.compiler.target>
        <!-- Setting this property using archetype-metadata.xml requiredPorperty
            so that generated project uses correct version of richfaces.
        -->
        <org.richfaces.version>4.5.17.Final</org.richfaces.version>
        <version.jsp-api>2.1</version.jsp-api>
        <version.jstl-api>1.2</version.jstl-api>
        <version.servlet-api>3.0.1</version.servlet-api>
        <version.el-api>2.2</version.el-api>
    </properties>

    <build>
        <finalName>new_project</finalName>
        <plugins>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>2.3.2</version>
            </plugin>
            <plugin>
                <artifactId>maven-war-plugin</artifactId>
                <version>2.3</version>
                <configuration>
                    <warName>${project.artifactId}</warName>
                </configuration>
            </plugin>
        </plugins>
    </build>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.richfaces</groupId>
                <artifactId>richfaces-cache-bom</artifactId>
                <type>pom</type>
                <version>${org.richfaces.version}</version>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>org.richfaces</groupId>
                <artifactId>richfaces-build</artifactId>
                <type>pom</type>
                <version>${org.richfaces.version}</version>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <dependency>
            <groupId>org.richfaces</groupId>
            <artifactId>richfaces</artifactId>
        </dependency>
        <dependency>
            <groupId>javax.faces</groupId>
            <artifactId>javax.faces-api</artifactId>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.glassfish</groupId>
            <artifactId>javax.faces</artifactId>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>${version.servlet-api}</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet.jsp</groupId>
            <artifactId>jsp-api</artifactId>
            <version>2.2</version>
        </dependency>
        <dependency>
            <groupId>javax.el</groupId>
            <artifactId>el-api</artifactId>
            <version>${version.el-api}</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet.jsp.jstl</groupId>
            <artifactId>jstl-api</artifactId>
        </dependency>


        <dependency>
            <groupId>net.sf.ehcache</groupId>
            <artifactId>ehcache</artifactId>
        </dependency>
    </dependencies>

    <profiles>
        <profile>
            <id>jee6</id>
            <build>
                <plugins>
                    <plugin>
                        <artifactId>maven-war-plugin</artifactId>
                        <configuration>
                            <webappDirectory>${project.build.directory}/${project.build.finalName}-jee6</webappDirectory>
                            <classifier>jee6</classifier>
                        </configuration>
                    </plugin>
                </plugins>
            </build>

            <dependencies>
                <dependency>
                    <groupId>javax.faces</groupId>
                    <artifactId>javax.faces-api</artifactId>
                    <scope>provided</scope>
                </dependency>
                <dependency>
                    <groupId>org.glassfish</groupId>
                    <artifactId>javax.faces</artifactId>
                    <scope>provided</scope>
                </dependency>
                <dependency>
                    <groupId>javax.transaction</groupId>
                    <artifactId>jta</artifactId>
                    <version>1.1</version>
                    <scope>provided</scope>
                </dependency>
            </dependencies>
        </profile>
        <profile>
            <id>release</id>
            <build>
                <plugins>
                    <plugin>
                        <artifactId>maven-war-plugin</artifactId>
                        <executions>
                            <execution>
                                <id>jee6</id>
                                <phase>package</phase>
                                <goals>
                                    <goal>war</goal>
                                </goals>

                            </execution>
                        </executions>
                    </plugin>
                </plugins>
            </build>
        </profile>
    </profiles>
</project>


Set " <Host appBase="webapps" autoDeploy="false" name="localhost" unpackWARs="false">" in the server.xml of the tomcat. 

1) copy the applications war file to the webapps folder and start the tomcat. When accessing the index.xhtml you can see that richfaces is not loaded correctly. 

2) copy a the unzipped war file to the webapps folder an start the server.When accessing the index.xhtml you can see that richfaces is loaded correctly. 

To make the difference more clear you can add a "<a4j:log/>" to the index.xhtml. 

Regards,
Vincent
Comment 1 Mark Thomas 2017-04-04 14:12:25 UTC
Thanks for the report and the test case.

This has been fixed in:
- trunk for 9.0.0.M20 onwards
- 8.5.x for 8.5.14 onwards
- 8.0.x for 8.0.44 onwards

7.0.x is not affected