Skip to content

Commit 8e0ab78

Browse files
committed
JIRA-77: Make it simpler to debug a JIRA content parsing problem
* Fix issue with authentification * Refactored exisiting code to be cleaner (removed FIXME)
1 parent 984f7dc commit 8e0ab78

File tree

6 files changed

+90
-38
lines changed

6 files changed

+90
-38
lines changed

jira-macro/jira-macro-charts/src/main/java/org/xwiki/contrib/jira/charts/internal/AbstractJIRAChartDataFetcher.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import org.xwiki.contrib.jira.charts.internal.source.AbstractJIRADataSource;
3232
import org.xwiki.contrib.jira.config.JIRAServer;
3333
import org.xwiki.contrib.jira.macro.internal.HTTPJIRAFetcher;
34+
import org.xwiki.contrib.jira.macro.internal.JIRAConnectionException;
3435
import org.xwiki.contrib.jira.macro.internal.JIRAURLHelper;
3536
import org.xwiki.contrib.jira.macro.internal.source.JIRAServerResolver;
3637
import org.xwiki.rendering.macro.MacroExecutionException;
@@ -74,7 +75,7 @@ public U fetch(T parameters, Class<U> expectedType) throws MacroExecutionExcepti
7475
String chartURL = this.urlHelper.getChartURL(server, getGadgetType(), parametersList);
7576
try {
7677
return this.httpjiraFetcher.fetchJSON(chartURL, server, expectedType);
77-
} catch (Exception e) {
78+
} catch (JIRAConnectionException e) {
7879
throw new MacroExecutionException(
7980
String.format("Error when trying to get data for pie chart from URL [%s].", chartURL), e);
8081
}

jira-macro/jira-macro-common/src/main/java/org/xwiki/contrib/jira/macro/internal/ErrorMessageExtractor.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
import java.io.IOException;
2323
import java.io.InputStream;
24+
import java.nio.charset.StandardCharsets;
2425
import java.util.regex.Matcher;
2526
import java.util.regex.Pattern;
2627

@@ -43,7 +44,7 @@ public class ErrorMessageExtractor
4344
String extract(InputStream contentStream) throws IOException
4445
{
4546
String result;
46-
String content = IOUtils.toString(contentStream, "UTF-8");
47+
String content = IOUtils.toString(contentStream, StandardCharsets.UTF_8);
4748
Matcher matcher = PATTERN.matcher(content);
4849
if (matcher.find()) {
4950
result = matcher.group(1);

jira-macro/jira-macro-common/src/main/java/org/xwiki/contrib/jira/macro/internal/HTTPJIRAFetcher.java

Lines changed: 27 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -19,17 +19,16 @@
1919
*/
2020
package org.xwiki.contrib.jira.macro.internal;
2121

22-
import java.io.IOException;
2322
import java.io.InputStream;
2423
import java.net.MalformedURLException;
2524
import java.net.URL;
2625
import java.nio.charset.StandardCharsets;
27-
import java.util.function.Function;
2826

2927
import javax.inject.Singleton;
3028

3129
import org.apache.commons.io.IOUtils;
3230
import org.apache.commons.lang3.StringUtils;
31+
import org.apache.commons.lang3.function.FailableFunction;
3332
import org.apache.http.HttpHost;
3433
import org.apache.http.auth.AuthScope;
3534
import org.apache.http.auth.UsernamePasswordCredentials;
@@ -44,7 +43,6 @@
4443
import org.apache.http.impl.client.CloseableHttpClient;
4544
import org.apache.http.impl.client.HttpClientBuilder;
4645
import org.jdom2.Document;
47-
import org.jdom2.JDOMException;
4846
import org.jdom2.input.SAXBuilder;
4947
import org.xwiki.component.annotation.Component;
5048
import org.xwiki.contrib.jira.config.JIRAServer;
@@ -70,21 +68,18 @@ public class HTTPJIRAFetcher
7068
* @param jiraServer the jira server data containing optional credentials (used to setup preemptive basic
7169
* authentication
7270
* @return the {@link Document} object containing the XML data
73-
* @throws Exception if an error happened during the fetch or if the passed URL is malformed
71+
* @throws JIRAConnectionException if an error happened during the fetch or if the passed URL is malformed
7472
*/
75-
public Document fetch(String urlString, JIRAServer jiraServer) throws Exception
73+
public Document fetch(String urlString, JIRAServer jiraServer) throws JIRAConnectionException
7674
{
77-
return performRequest(urlString, jiraServer, is -> {
78-
try {
79-
Document document = createSAXBuilder().build(is);
80-
return document;
81-
} catch (JDOMException | IOException e) {
82-
// The XML has failed to be parsed, read it as plain text ans return it, for debugging purpose.
83-
String message = String.format("Failed to parse JIRA XML content [%s]", getRawJIRAResponse(urlString));
84-
// FIXME: this is ugly
85-
throw new RuntimeException(message, e);
86-
}
87-
});
75+
try {
76+
return performRequest(urlString, jiraServer, is -> createSAXBuilder().build(is));
77+
} catch (Exception e) {
78+
// The XML has failed to be parsed, read it as plain text and return it, for debugging purpose.
79+
String message = String.format("Failed to parse JIRA XML content [%s]",
80+
getRawJIRAResponse(urlString, jiraServer));
81+
throw new JIRAConnectionException(message, e);
82+
}
8883
}
8984

9085
private ObjectMapper getObjectMapper()
@@ -103,34 +98,33 @@ private ObjectMapper getObjectMapper()
10398
* @param type the actual type to obtain when parsing the JSON
10499
* @return an instance of the POJO corresponding to the JSON answer
105100
* @param <T> the expected type
106-
* @throws Exception in case of problem when performing the request
101+
* @throws JIRAConnectionException in case of problem when performing the request
107102
*/
108-
public <T> T fetchJSON(String urlString, JIRAServer jiraServer, Class<T> type) throws Exception
103+
public <T> T fetchJSON(String urlString, JIRAServer jiraServer, Class<T> type) throws JIRAConnectionException
109104
{
110-
return performRequest(urlString, jiraServer, is -> {
111-
try {
112-
return getObjectMapper().readValue(is, type);
113-
} catch (IOException e) {
114-
// The JSON has failed to be parsed, read it as plain text and return it, for debugging purpose.
115-
String message = String.format("Failed to parse JIRA JSON content [%s]", getRawJIRAResponse(urlString));
116-
// FIXME: this is ugly
117-
throw new RuntimeException(message, e);
118-
}
119-
});
105+
try {
106+
return performRequest(urlString, jiraServer, is -> getObjectMapper().readValue(is, type));
107+
} catch (Exception e) {
108+
// The JSON has failed to be parsed, read it as plain text and return it, for debugging purpose.
109+
String message = String.format("Failed to parse JIRA JSON content [%s]",
110+
getRawJIRAResponse(urlString, jiraServer));
111+
throw new JIRAConnectionException(message, e);
112+
}
120113
}
121114

122-
private String getRawJIRAResponse(String urlString)
115+
private String getRawJIRAResponse(String urlString, JIRAServer jiraServer)
123116
{
124117
String message;
125118
try {
126-
message = IOUtils.toString(new URL(urlString), StandardCharsets.UTF_8);
127-
} catch (IOException ee) {
119+
message = performRequest(urlString, jiraServer, is -> IOUtils.toString(is, StandardCharsets.UTF_8));
120+
} catch (Exception e) {
128121
message = String.format("Failed to get JIRA content for [%s]", urlString);
129122
}
130123
return message;
131124
}
132125

133-
private <T> T performRequest(String url, JIRAServer jiraServer, Function<InputStream, T> callback) throws Exception
126+
private <T> T performRequest(String url, JIRAServer jiraServer,
127+
FailableFunction<InputStream, T, Exception> callback) throws Exception
134128
{
135129
HttpGet httpGet = new HttpGet(url);
136130
CloseableHttpClient httpClient = createHttpClientBuilder().build();
@@ -145,7 +139,7 @@ private <T> T performRequest(String url, JIRAServer jiraServer, Function<InputSt
145139
} else {
146140
// The error message is in the HTML. We extract it to perform some good error-reporting, by extracting
147141
// it from the <h1> tag.
148-
throw new Exception(String.format("Error = [%s]. URL = [%s]",
142+
throw new JIRAConnectionException(String.format("Error = [%s]. URL = [%s]",
149143
EXTRACTOR.extract(response.getEntity().getContent()), httpGet.getURI().toString()));
150144
}
151145
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/*
2+
* See the NOTICE file distributed with this work for additional
3+
* information regarding copyright ownership.
4+
*
5+
* This is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU Lesser General Public License as
7+
* published by the Free Software Foundation; either version 2.1 of
8+
* the License, or (at your option) any later version.
9+
*
10+
* This software is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13+
* Lesser General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU Lesser General Public
16+
* License along with this software; if not, write to the Free
17+
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
18+
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
19+
*/
20+
package org.xwiki.contrib.jira.macro.internal;
21+
22+
/**
23+
* Exception thrown when there is an error when accessing the remote JIRA server.
24+
*
25+
* @version $Id$
26+
* @since 10.1.3
27+
*/
28+
public class JIRAConnectionException extends Exception
29+
{
30+
private static final long serialVersionUID = -2356790547774480310L;
31+
32+
/**
33+
* Construct a new {@code MentionsException} with the specified detail message.
34+
*
35+
* @param message the detail message. The detail message is saved for later retrieval by the {@link
36+
* #getMessage()}
37+
*/
38+
public JIRAConnectionException(String message)
39+
{
40+
super(message);
41+
}
42+
43+
/**
44+
* Constructs a new {@code MentionsException} with the specified detail message and cause.
45+
*
46+
* @param message the detail message. The detail message is saved for later retrieval by the {@link
47+
* #getMessage()}
48+
* @param cause the cause (which is saved for later retrieval by the {@link #getCause()} method). (A null value
49+
* is permitted, and indicates that the cause is nonexistent or unknown.)
50+
*/
51+
public JIRAConnectionException(String message, Throwable cause)
52+
{
53+
super(message, cause);
54+
}
55+
}

jira-macro/jira-macro-common/src/test/java/org/xwiki/contrib/jira/macro/internal/HTTPJIRAFetcherTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ void xmlError()
7878
.withHeader("Content-Type", "text/xml")
7979
.withBodyFile("input.xml")));
8080

81-
Throwable exception = assertThrows(RuntimeException.class, () -> {
81+
Throwable exception = assertThrows(JIRAConnectionException.class, () -> {
8282
this.jiraFetcher.fetch("http://localhost:8889/sr/jira.issueviews:searchrequest-xml/temp/SearchRequest.xml?jqlQuery=whatever", new JIRAServer("http://localhost:8889", "user", "pass"));
8383
});
8484
assertEquals("Failed to parse JIRA XML content [<!--\n"

jira-macro/jira-macro-default/src/main/java/org/xwiki/contrib/jira/macro/internal/source/AbstractJIRADataSource.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import org.jdom2.Element;
2929
import org.xwiki.contrib.jira.config.JIRAServer;
3030
import org.xwiki.contrib.jira.macro.internal.HTTPJIRAFetcher;
31+
import org.xwiki.contrib.jira.macro.internal.JIRAConnectionException;
3132
import org.xwiki.contrib.jira.macro.internal.JIRAURLHelper;
3233
import org.xwiki.contrib.jira.macro.JIRADataSource;
3334
import org.xwiki.contrib.jira.macro.JIRAField;
@@ -83,7 +84,7 @@ public Document getXMLDocument(JIRAServer jiraServer, String jqlQuery, int maxCo
8384
try {
8485
String urlString = this.urlHelper.getSearchURL(jiraServer, jqlQuery, maxCount);
8586
document = this.jiraFetcher.fetch(urlString, jiraServer);
86-
} catch (Exception e) {
87+
} catch (JIRAConnectionException e) {
8788
throw new MacroExecutionException(String.format("Failed to retrieve JIRA data from [%s] for JQL [%s]",
8889
jiraServer.getURL(), jqlQuery), e);
8990
}
@@ -92,7 +93,7 @@ public Document getXMLDocument(JIRAServer jiraServer, String jqlQuery, int maxCo
9293

9394
/**
9495
* @param parameters the macro's parameters
95-
* @return the url to the JIRA instance (eg "http://jira.xwiki.org")
96+
* @return the url to the JIRA instance (e.g. {@code http://jira.xwiki.org"})
9697
* @throws MacroExecutionException if no URL has been specified (either in the macro parameter or configuration)
9798
*/
9899
protected JIRAServer getJIRAServer(JIRAMacroParameters parameters) throws MacroExecutionException

0 commit comments

Comments
 (0)