Skip to content

Commit 0982ba4

Browse files
authored
Queue browse improvements in webconsole (apache#1938) (apache#1942)
This change makes sure we always use the correct content type for the output based on the configured view and also escape xml content when displaying. (cherry picked from commit a240dba)
1 parent 1dabafb commit 0982ba4

7 files changed

Lines changed: 169 additions & 19 deletions

File tree

activemq-web/pom.xml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,13 @@
8787
<artifactId>websocket-jetty-server</artifactId>
8888
</dependency>
8989

90+
<!-- Just used for testing -->
91+
<dependency>
92+
<groupId>org.apache.commons</groupId>
93+
<artifactId>commons-text</artifactId>
94+
<scope>test</scope>
95+
</dependency>
96+
9097
<!-- Rome RSS Reader -->
9198
<dependency>
9299
<groupId>com.rometools</groupId>
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
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+
package org.apache.activemq.web.util;
18+
19+
import java.util.Collections;
20+
import java.util.LinkedHashMap;
21+
import java.util.Map;
22+
import java.util.Map.Entry;
23+
24+
public class ViewUtils {
25+
26+
public static final String AMP = "&";
27+
public static final String QUOTE = "\"";
28+
public static final String LT = "<";
29+
public static final String GT = ">";
30+
public static final String APOS = "'";
31+
32+
public static final String XML_ESCAPED_AMP = "&amp;";
33+
public static final String XML_ESCAPED_QUOTE = "&quot;";
34+
public static final String XML_ESCAPED_LT = "&lt;";
35+
public static final String XML_ESCAPED_GT = "&gt;";
36+
public static final String XML_ESCAPED_APOS = "&apos;";
37+
38+
public static final Map<String, String> XML_ESCAPE_MAPPINGS;
39+
40+
static {
41+
// order matters for processing so use a linked map
42+
Map<String,String> mappings = new LinkedHashMap<>();
43+
mappings.put(AMP, XML_ESCAPED_AMP);
44+
mappings.put(LT, XML_ESCAPED_LT);
45+
mappings.put(GT, XML_ESCAPED_GT);
46+
mappings.put(QUOTE, XML_ESCAPED_QUOTE);
47+
mappings.put(APOS, XML_ESCAPED_APOS);
48+
49+
XML_ESCAPE_MAPPINGS = Collections.unmodifiableMap(mappings);
50+
}
51+
52+
53+
public static String escapeXml(String input) {
54+
if (input == null) {
55+
return null;
56+
}
57+
58+
String escaped = input;
59+
for (Entry<String, String> entry : XML_ESCAPE_MAPPINGS.entrySet()) {
60+
escaped = escaped.replace(entry.getKey(), entry.getValue());
61+
}
62+
63+
return escaped;
64+
}
65+
66+
}

activemq-web/src/main/java/org/apache/activemq/web/view/RssMessageRenderer.java

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ public class RssMessageRenderer extends SimpleMessageRenderer {
4949
private String feedType = "rss_2.0";
5050
private SyndFeed feed;
5151
private String description = "This feed is auto-generated by Apache ActiveMQ";
52-
private String entryContentType = "text/plain";
52+
private static final String ENTRY_CONTENT_TYPE = "text/plain";
5353

5454
public void renderMessage(PrintWriter writer, HttpServletRequest request, HttpServletResponse response, QueueBrowser browser, Message message) throws JMSException {
5555
SyndFeed feed = getFeed(browser, request);
@@ -79,11 +79,7 @@ public void setFeedType(String feedType) {
7979
}
8080

8181
public String getEntryContentType() {
82-
return entryContentType;
83-
}
84-
85-
public void setEntryContentType(String entryContentType) {
86-
this.entryContentType = entryContentType;
82+
return ENTRY_CONTENT_TYPE;
8783
}
8884

8985
// Implementation methods
@@ -122,7 +118,7 @@ protected SyndEntry createEntry(QueueBrowser browser, Message message, HttpServl
122118

123119
protected SyndContent createEntryContent(QueueBrowser browser, Message message, HttpServletRequest request) throws JMSException {
124120
SyndContent description = new SyndContentImpl();
125-
description.setType(entryContentType);
121+
description.setType(getEntryContentType());
126122

127123
if (message instanceof TextMessage) {
128124
String text = ((TextMessage)message).getText();

activemq-web/src/main/java/org/apache/activemq/web/view/SimpleMessageRenderer.java

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import jakarta.servlet.ServletException;
2727
import jakarta.servlet.http.HttpServletRequest;
2828
import jakarta.servlet.http.HttpServletResponse;
29+
import org.apache.activemq.web.util.ViewUtils;
2930

3031
/**
3132
* A simple rendering of the contents of a queue appear as a list of message
@@ -35,11 +36,12 @@
3536
*/
3637
public class SimpleMessageRenderer implements MessageRenderer {
3738

38-
private String contentType = "text/xml";
39+
protected static final String DEFAULT_CONTENT_TYPE = "text/xml";
40+
3941
private int maxMessages;
4042

4143
public void renderMessages(HttpServletRequest request, HttpServletResponse response, QueueBrowser browser) throws IOException, JMSException, ServletException {
42-
// lets use XML by default
44+
// XML is used by default unless a child class overrides this method
4345
response.setContentType(getContentType());
4446
PrintWriter writer = response.getWriter();
4547
printHeader(writer, browser, request);
@@ -53,10 +55,10 @@ public void renderMessages(HttpServletRequest request, HttpServletResponse respo
5355
printFooter(writer, browser, request);
5456
}
5557

56-
public void renderMessage(PrintWriter writer, HttpServletRequest request, HttpServletResponse response, QueueBrowser browser, Message message) throws JMSException, ServletException {
58+
public void renderMessage(PrintWriter writer, HttpServletRequest request, HttpServletResponse response, QueueBrowser browser, Message message) throws JMSException {
5759
// lets just write the message IDs for now
5860
writer.print("<message id='");
59-
writer.print(message.getJMSMessageID());
61+
writer.print(ViewUtils.escapeXml(message.getJMSMessageID()));
6062
writer.println("'/>");
6163
}
6264

@@ -71,25 +73,21 @@ public void setMaxMessages(int maxMessages) {
7173
}
7274

7375
public String getContentType() {
74-
return contentType;
75-
}
76-
77-
public void setContentType(String contentType) {
78-
this.contentType = contentType;
76+
return DEFAULT_CONTENT_TYPE;
7977
}
8078

8179
// Implementation methods
8280
// -------------------------------------------------------------------------
8381

84-
protected void printHeader(PrintWriter writer, QueueBrowser browser, HttpServletRequest request) throws IOException, JMSException, ServletException {
82+
protected void printHeader(PrintWriter writer, QueueBrowser browser, HttpServletRequest request) throws IOException, JMSException {
8583
writer.println("");
8684
writer.print("<messages queue='");
87-
writer.print(browser.getQueue());
85+
writer.print(ViewUtils.escapeXml(String.valueOf(browser.getQueue())));
8886
writer.print("'");
8987
String selector = browser.getMessageSelector();
9088
if (selector != null) {
9189
writer.print(" selector='");
92-
writer.print(selector);
90+
writer.print(ViewUtils.escapeXml(selector));
9391
writer.print("'");
9492
}
9593
writer.println(">");
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
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+
package org.apache.activemq.web.util;
18+
19+
import static org.junit.Assert.assertEquals;
20+
21+
import java.io.IOException;
22+
import java.nio.file.Files;
23+
import java.nio.file.Path;
24+
import org.apache.commons.text.StringEscapeUtils;
25+
import org.junit.Test;
26+
27+
public class ViewUtilsTest {
28+
29+
@Test
30+
public void testXmlEscape() throws IOException {
31+
final String original = Files.readString(Path.of("src/test/resources/activemq.xml"));
32+
final String escaped = ViewUtils.escapeXml(original);
33+
34+
// Verify that our escape method matches StringEscapeUtils
35+
assertEquals(StringEscapeUtils.escapeXml11(original), ViewUtils.escapeXml(original));
36+
// Verify if we unescape we get back the original
37+
assertEquals(original, StringEscapeUtils.unescapeXml(escaped));
38+
}
39+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
~ Licensed to the Apache Software Foundation (ASF) under one or more
4+
~ contributor license agreements. See the NOTICE file distributed with
5+
~ this work for additional information regarding copyright ownership.
6+
~ The ASF licenses this file to You under the Apache License, Version 2.0
7+
~ (the "License"); you may not use this file except in compliance with
8+
~ the License. You may obtain a copy of the License at
9+
~
10+
~ http://www.apache.org/licenses/LICENSE-2.0
11+
~
12+
~ Unless required by applicable law or agreed to in writing, software
13+
~ distributed under the License is distributed on an "AS IS" BASIS,
14+
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
~ See the License for the specific language governing permissions and
16+
~ limitations under the License.
17+
-->
18+
<!-- START SNIPPET: xbean -->
19+
<beans
20+
xmlns="http://www.springframework.org/schema/beans"
21+
xmlns:amq="http://activemq.apache.org/schema/core"
22+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
23+
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
24+
http://activemq.apache.org/schema/core http://activemq.apache.org/schema/core/activemq-core.xsd">
25+
26+
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"/>
27+
28+
<broker useJmx="false" xmlns="http://activemq.apache.org/schema/core" persistent="false">
29+
30+
<transportConnectors>
31+
<transportConnector uri="nio://localhost:61616?wireFormat.maxFrameSize=1048576" />
32+
</transportConnectors>
33+
34+
</broker>
35+
36+
</beans>
37+
<!-- END SNIPPET: xbean -->

pom.xml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
<commons-io-version>2.21.0</commons-io-version>
5252
<commons-logging-version>1.3.5</commons-logging-version>
5353
<commons-pool2-version>2.13.1</commons-pool2-version>
54+
<commons-text-version>1.15.0</commons-text-version>
5455
<directory-version>2.0.0.AM25</directory-version>
5556
<ecj.version>3.44.0</ecj.version>
5657
<ftpserver-version>1.1.1</ftpserver-version>
@@ -911,6 +912,12 @@
911912
<version>${commons-io-version}</version>
912913
</dependency>
913914

915+
<dependency>
916+
<groupId>org.apache.commons</groupId>
917+
<artifactId>commons-text</artifactId>
918+
<version>${commons-text-version}</version>
919+
</dependency>
920+
914921
<!-- ACTIVEMQ-WEB Specific Dependencies -->
915922
<dependency>
916923
<groupId>com.rometools</groupId>

0 commit comments

Comments
 (0)