Merge pull request #920 from apache/fix/WW-5419-tiles

[WW-5419] Fixes support for loading Tiles definitions
diff --git a/plugins/tiles/pom.xml b/plugins/tiles/pom.xml
index 208e50f..845062b 100644
--- a/plugins/tiles/pom.xml
+++ b/plugins/tiles/pom.xml
@@ -102,6 +102,16 @@
             <artifactId>log4j-jcl</artifactId>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>org.assertj</groupId>
+            <artifactId>assertj-core</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-test</artifactId>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
     <properties>
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
diff --git a/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesContainerFactory.java b/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesContainerFactory.java
index ed03f82..4cda085 100644
--- a/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesContainerFactory.java
+++ b/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesContainerFactory.java
@@ -68,10 +68,7 @@
 import javax.el.ResourceBundleELResolver;
 import javax.servlet.jsp.JspFactory;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collection;
-import java.util.Collections;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
@@ -105,19 +102,16 @@
 
     /**
      * Default pattern to be used to collect Tiles definitions if user didn't configure any
+     */
+    public static final Set<String> TILES_DEFAULT_PATTERNS = TextParseUtil.commaDelimitedStringToSet("*tiles*.xml");
+
+    /**
+     * Default pattern to be used to collect Tiles definitions if user didn't configure any
      *
      * @deprecated since Struts 6.4.0, use {@link #TILES_DEFAULT_PATTERNS} instead
      */
     @Deprecated
-    public static final String TILES_DEFAULT_PATTERN = "/WEB-INF/**/tiles*.xml,classpath*:META-INF/**/tiles*.xml";
-
-    /**
-     * Default pattern to be used to collect Tiles definitions if user didn't configure any
-     */
-    public static final Set<String> TILES_DEFAULT_PATTERNS = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(
-            "/WEB-INF/**/tiles*.xml",
-            "classpath*:META-INF/**/tiles*.xml"
-    )));
+    public static final String TILES_DEFAULT_PATTERN = String.join(",", TILES_DEFAULT_PATTERNS);
 
     /**
      * Supported expression languages
diff --git a/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsWildcardServletApplicationContext.java b/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsWildcardServletApplicationContext.java
index 7af6069..2a30eb6 100644
--- a/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsWildcardServletApplicationContext.java
+++ b/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsWildcardServletApplicationContext.java
@@ -43,7 +43,7 @@
 
     private static final Logger LOG = LogManager.getLogger(StrutsWildcardServletApplicationContext.class);
 
-    private ResourceFinder finder;
+    private final ResourceFinder finder;
 
     public StrutsWildcardServletApplicationContext(ServletContext context) {
         super(context);
@@ -64,16 +64,15 @@
         }
 
         try {
-            Enumeration<URL> resources = getClass().getClassLoader().getResources("/");
+            Enumeration<URL> resources = getClass().getClassLoader().getResources("");
             while (resources.hasMoreElements()) {
-                URL resource = resources.nextElement();
-                urls.add(resource);
+                urls.add(resources.nextElement());
             }
         } catch (IOException e) {
             throw new ConfigurationException(e);
         }
 
-        finder = new ResourceFinder(urls.toArray(new URL[urls.size()]));
+        finder = new ResourceFinder(urls.toArray(new URL[0]));
     }
 
     public Collection<ApplicationResource> getResources(String path) {
diff --git a/plugins/tiles/src/test/java/org/apache/struts2/tiles/StrutsTilesContainerFactoryTest.java b/plugins/tiles/src/test/java/org/apache/struts2/tiles/StrutsTilesContainerFactoryTest.java
index 122bfe5..944aac2 100644
--- a/plugins/tiles/src/test/java/org/apache/struts2/tiles/StrutsTilesContainerFactoryTest.java
+++ b/plugins/tiles/src/test/java/org/apache/struts2/tiles/StrutsTilesContainerFactoryTest.java
@@ -34,8 +34,8 @@
 import org.junit.Before;
 import org.junit.Test;
 
-import javax.servlet.ServletContext;
 import javax.servlet.jsp.JspFactory;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
@@ -64,12 +64,11 @@
                 Objects.requireNonNull(getClass().getResource("/org/apache/tiles/core/config/tiles-defs.xml"))
         );
         ApplicationResource classpathResource = new URLApplicationResource(
-                "/org/apache/tiles/core/config/defs1.xml",
-                Objects.requireNonNull(getClass().getResource("/org/apache/tiles/core/config/defs1.xml"))
+                "/org/apache/tiles/core/config/tiles_defs1.xml",
+                Objects.requireNonNull(getClass().getResource("/org/apache/tiles/core/config/tiles_defs1.xml"))
         );
         when(applicationContext.getInitParams()).thenReturn(Collections.emptyMap());
-        when(applicationContext.getResources("/WEB-INF/**/tiles*.xml")).thenReturn(Collections.singleton(pathResource));
-        when(applicationContext.getResources("classpath*:META-INF/**/tiles*.xml")).thenReturn(Collections.singleton(classpathResource));
+        when(applicationContext.getResources("*tiles*.xml")).thenReturn(Arrays.asList(pathResource, classpathResource));
 
         List<ApplicationResource> resources = factory.getSources(applicationContext);
         assertEquals("The urls list is not two-sized", 2, resources.size());
diff --git a/plugins/tiles/src/test/java/org/apache/struts2/tiles/StrutsWildcardServletApplicationContextTest.java b/plugins/tiles/src/test/java/org/apache/struts2/tiles/StrutsWildcardServletApplicationContextTest.java
new file mode 100644
index 0000000..972ed70
--- /dev/null
+++ b/plugins/tiles/src/test/java/org/apache/struts2/tiles/StrutsWildcardServletApplicationContextTest.java
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.struts2.tiles;
+
+import org.apache.tiles.core.definition.DefinitionsFactory;
+import org.apache.tiles.request.ApplicationResource;
+import org.junit.Before;
+import org.junit.Test;
+import org.springframework.core.io.FileSystemResource;
+import org.springframework.core.io.FileUrlResource;
+import org.springframework.core.io.Resource;
+import org.springframework.core.io.ResourceLoader;
+import org.springframework.mock.web.MockServletContext;
+
+import javax.servlet.ServletContext;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Collection;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class StrutsWildcardServletApplicationContextTest {
+
+    private ServletContext context;
+
+    @Before
+    public void setUp() throws Exception {
+        URL resource = getClass().getResource("/");
+        context = new MockServletContext(resource.getPath(), new ResourceLoader() {
+            @Override
+            public Resource getResource(String location) {
+                try {
+                    String finalLocation = location.replaceAll("//", "/");
+                    if (finalLocation.endsWith("/")) {
+                        return new FileSystemResource(finalLocation);
+                    }
+                    return new FileUrlResource(finalLocation);
+                } catch (MalformedURLException e) {
+                    return null;
+                }
+            }
+
+            @Override
+            public ClassLoader getClassLoader() {
+                return StrutsWildcardServletApplicationContextTest.class.getClassLoader();
+            }
+        });
+    }
+
+    @Test
+    public void wildcardSupport() {
+        StrutsWildcardServletApplicationContext applicationContext = new StrutsWildcardServletApplicationContext(context);
+
+        Collection<ApplicationResource> resources = applicationContext.getResources("*tiles*.xml");
+
+        assertThat(resources)
+                .hasSize(1)
+                .extracting(ApplicationResource::getLocalePath)
+                .first().asString()
+                .endsWith("/WEB-INF/tiles.xml");
+    }
+
+}
\ No newline at end of file
diff --git a/plugins/tiles/src/test/resources/WEB-INF/tiles.xml b/plugins/tiles/src/test/resources/WEB-INF/tiles.xml
new file mode 100644
index 0000000..9fc36a4
--- /dev/null
+++ b/plugins/tiles/src/test/resources/WEB-INF/tiles.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="ISO-8859-1" ?>
+<!--
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+-->
+
+<!DOCTYPE tiles-definitions PUBLIC
+        "-//Apache Software Foundation//DTD Tiles Configuration 3.0//EN"
+        "http://tiles.apache.org/dtds/tiles-config_3_0.dtd">
+
+<tiles-definitions>
+
+    <!-- Doc index page description  -->
+    <definition name="top.mainLayout" template="/layout/classicLayout.jsp">
+        <put-attribute name="title" value="Tiles Library Documentation"/>
+        <put-attribute name="header" value="/common/header.jsp"/>
+        <put-attribute name="menu" value="doc.menu.main"/>
+        <put-attribute name="footer" value="/common/footer.jsp"/>
+        <put-attribute name="body" value="doc.portal.body"/>
+        <put-attribute name="bean" value="This is an object" type="object"/>
+    </definition>
+
+</tiles-definitions>
diff --git a/plugins/tiles/src/test/resources/org/apache/tiles/core/config/tiles_defs1.xml b/plugins/tiles/src/test/resources/org/apache/tiles/core/config/tiles_defs1.xml
new file mode 100644
index 0000000..621d7ce
--- /dev/null
+++ b/plugins/tiles/src/test/resources/org/apache/tiles/core/config/tiles_defs1.xml
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="ISO-8859-1" ?>
+<!--
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+-->
+
+ <!DOCTYPE tiles-definitions PUBLIC
+       "-//Apache Software Foundation//DTD Tiles Configuration 3.0//EN"
+       "http://tiles.apache.org/dtds/tiles-config_3_0.dtd">
+
+<!-- Definitions for Tiles documentation   -->
+
+<tiles-definitions>
+
+  <!-- =======================================================  -->
+  <!-- Master definition  									-->
+  <!-- =======================================================  -->
+
+  <!-- This definition will remain the same for all locales -->
+  <definition name="test.common" template="/test.jsp">
+      <put-attribute name="country" value="none"/>
+    <put-attribute name="title"  value="Common Definition" />
+    <put-attribute name="header" value="/common/header.jsp" />
+    <put-attribute name="menu"   value="doc.menu.main" />
+    <put-attribute name="footer" value="/common/footer.jsp" />
+    <put-attribute name="body"   value="doc.portal.body" />
+  </definition>
+
+  <!-- Doc index page description  -->
+  <definition name="test.def1" template="/test.jsp">
+          <put-attribute name="country" value="default"/>
+    <put-attribute name="title"  value="Tiles Library Documentation" />
+    <put-attribute name="header" value="/common/header.jsp" />
+    <put-attribute name="menu"   value="doc.menu.main" />
+    <put-attribute name="footer" value="/common/footer.jsp" />
+    <put-attribute name="body"   value="doc.portal.body" />
+  </definition>
+
+  <!-- This definition will be extended -->
+  <definition name="test.def.toextend" template="/test.jsp">
+    <put-attribute name="country" value="default"/>
+    <put-attribute name="title"  value="Definition to be extended" />
+    <put-attribute name="header" value="/common/header.jsp" />
+    <put-attribute name="menu"   value="doc.menu.main" />
+    <put-attribute name="footer" value="/common/footer.jsp" />
+    <put-attribute name="body"   value="doc.portal.body" />
+  </definition>
+
+  <!-- This definition will be overridden -->
+  <definition name="test.def.overridden" template="/test.jsp">
+    <put-attribute name="country" value="default"/>
+    <put-attribute name="title"  value="Definition to be overridden" />
+    <put-attribute name="header" value="/common/header.jsp" />
+    <put-attribute name="menu"   value="doc.menu.main" />
+    <put-attribute name="footer" value="/common/footer.jsp" />
+    <put-attribute name="body"   value="doc.portal.body" />
+  </definition>
+
+</tiles-definitions>