Skip to content

Commit 9dafcb5

Browse files
committed
Fix BZ 69995: Generate _jspx_dependants.put() calls in deterministic order
1 parent 7557c61 commit 9dafcb5

File tree

7 files changed

+118
-2
lines changed

7 files changed

+118
-2
lines changed

java/org/apache/jasper/compiler/Generator.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -552,7 +552,7 @@ private void genPreambleStaticInitializers() {
552552
if (!dependants.isEmpty()) {
553553
out.printil("static {");
554554
out.pushIndent();
555-
out.printin("_jspx_dependants = new java.util.HashMap<java.lang.String,java.lang.Long>(");
555+
out.printin("_jspx_dependants = new java.util.LinkedHashMap<java.lang.String,java.lang.Long>(");
556556
out.print("" + dependants.size());
557557
out.println(");");
558558
for (Entry<String,Long> entry : dependants.entrySet()) {

java/org/apache/jasper/compiler/PageInfo.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import java.util.Deque;
2222
import java.util.HashMap;
2323
import java.util.HashSet;
24+
import java.util.LinkedHashMap;
2425
import java.util.LinkedList;
2526
import java.util.List;
2627
import java.util.Map;
@@ -111,7 +112,7 @@ public class PageInfo {
111112
this.jspPrefixMapper = new HashMap<>();
112113
this.xmlPrefixMapper = new HashMap<>();
113114
this.nonCustomTagPrefixMap = new HashMap<>();
114-
this.dependants = new HashMap<>();
115+
this.dependants = new LinkedHashMap<>();
115116
this.includePrelude = new ArrayList<>();
116117
this.includeCoda = new ArrayList<>();
117118
this.pluginDcls = new ArrayList<>();

test/org/apache/jasper/compiler/TestGenerator.java

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,17 @@
2222
import java.beans.PropertyEditorSupport;
2323
import java.io.File;
2424
import java.io.IOException;
25+
import java.lang.reflect.Field;
2526
import java.nio.charset.CodingErrorAction;
27+
import java.util.ArrayList;
2628
import java.util.Date;
29+
import java.util.List;
30+
import java.util.Map;
2731
import java.util.Scanner;
2832

33+
import org.apache.jasper.compiler.JspRuntimeContext;
34+
import org.apache.jasper.servlet.JspServlet;
35+
2936
import jakarta.servlet.http.HttpServletResponse;
3037
import jakarta.servlet.jsp.JspException;
3138
import jakarta.servlet.jsp.PageContext;
@@ -828,6 +835,44 @@ public void testInclude01() throws Exception {
828835
doTestJsp("include-01.jsp");
829836
}
830837

838+
/*
839+
* Verify that _jspx_dependants entries appear in the same order as the
840+
* <%@ include file="..." %> directives in the source JSP, ensuring
841+
* reproducible builds (LinkedHashMap preserves insertion order).
842+
*/
843+
@Test
844+
public void testDependantsOrder() throws Exception {
845+
Tomcat tomcat = getTomcatInstanceTestWebapp(false, true);
846+
847+
ByteChunk body = new ByteChunk();
848+
int rc = getUrl("http://localhost:" + getPort() +
849+
"/test/jsp/generator/dependants-order.jsp", body, null);
850+
Assert.assertEquals(body.toString(), HttpServletResponse.SC_OK, rc);
851+
852+
// JSP classes are loaded by a per-JSP JasperLoader child classloader,
853+
// not by the webapp classloader. Retrieve the dependants map through
854+
// the JspServletWrapper, which calls getDependants() on the live
855+
// servlet instance via the JspSourceDependent interface.
856+
Context ctx = (Context) tomcat.getHost().findChild("/test");
857+
Wrapper jspWrapper = (Wrapper) ctx.findChild("jsp");
858+
JspServlet jspServlet = (JspServlet) jspWrapper.getServlet();
859+
Field rctxtField = JspServlet.class.getDeclaredField("rctxt");
860+
rctxtField.setAccessible(true);
861+
JspRuntimeContext rctxt = (JspRuntimeContext) rctxtField.get(jspServlet);
862+
Map<String,Long> dependants = rctxt.getWrapper(
863+
"/jsp/generator/dependants-order.jsp").getDependants();
864+
865+
// Expect exactly the three fragments, in directive order: a, b, c.
866+
List<String> keys = new ArrayList<>(dependants.keySet());
867+
Assert.assertEquals(3, keys.size());
868+
Assert.assertTrue("a.jspf should precede b.jspf in _jspx_dependants",
869+
keys.indexOf("/jsp/generator/dependants-order-a.jspf") <
870+
keys.indexOf("/jsp/generator/dependants-order-b.jspf"));
871+
Assert.assertTrue("b.jspf should precede c.jspf in _jspx_dependants",
872+
keys.indexOf("/jsp/generator/dependants-order-b.jspf") <
873+
keys.indexOf("/jsp/generator/dependants-order-c.jspf"));
874+
}
875+
831876
@Test
832877
public void testSetProperty01() throws Exception {
833878
doTestJsp("setproperty-01.jsp");
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<%--
2+
Licensed to the Apache Software Foundation (ASF) under one or more
3+
contributor license agreements. See the NOTICE file distributed with
4+
this work for additional information regarding copyright ownership.
5+
The ASF licenses this file to You under the Apache License, Version 2.0
6+
(the "License"); you may not use this file except in compliance with
7+
the License. You may obtain a copy of the License at
8+
9+
http://www.apache.org/licenses/LICENSE-2.0
10+
11+
Unless required by applicable law or agreed to in writing, software
12+
distributed under the License is distributed on an "AS IS" BASIS,
13+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
See the License for the specific language governing permissions and
15+
limitations under the License.
16+
--%>
17+
a
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<%--
2+
Licensed to the Apache Software Foundation (ASF) under one or more
3+
contributor license agreements. See the NOTICE file distributed with
4+
this work for additional information regarding copyright ownership.
5+
The ASF licenses this file to You under the Apache License, Version 2.0
6+
(the "License"); you may not use this file except in compliance with
7+
the License. You may obtain a copy of the License at
8+
9+
http://www.apache.org/licenses/LICENSE-2.0
10+
11+
Unless required by applicable law or agreed to in writing, software
12+
distributed under the License is distributed on an "AS IS" BASIS,
13+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
See the License for the specific language governing permissions and
15+
limitations under the License.
16+
--%>
17+
b
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<%--
2+
Licensed to the Apache Software Foundation (ASF) under one or more
3+
contributor license agreements. See the NOTICE file distributed with
4+
this work for additional information regarding copyright ownership.
5+
The ASF licenses this file to You under the Apache License, Version 2.0
6+
(the "License"); you may not use this file except in compliance with
7+
the License. You may obtain a copy of the License at
8+
9+
http://www.apache.org/licenses/LICENSE-2.0
10+
11+
Unless required by applicable law or agreed to in writing, software
12+
distributed under the License is distributed on an "AS IS" BASIS,
13+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
See the License for the specific language governing permissions and
15+
limitations under the License.
16+
--%>
17+
c
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<%--
2+
Licensed to the Apache Software Foundation (ASF) under one or more
3+
contributor license agreements. See the NOTICE file distributed with
4+
this work for additional information regarding copyright ownership.
5+
The ASF licenses this file to You under the Apache License, Version 2.0
6+
(the "License"); you may not use this file except in compliance with
7+
the License. You may obtain a copy of the License at
8+
9+
http://www.apache.org/licenses/LICENSE-2.0
10+
11+
Unless required by applicable law or agreed to in writing, software
12+
distributed under the License is distributed on an "AS IS" BASIS,
13+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
See the License for the specific language governing permissions and
15+
limitations under the License.
16+
--%>
17+
<%@ include file="dependants-order-a.jspf" %>
18+
<%@ include file="dependants-order-b.jspf" %>
19+
<%@ include file="dependants-order-c.jspf" %>

0 commit comments

Comments
 (0)