PageRenderTime 49ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 1ms

/plugins/jira/src/java/edu/umd/cs/findbugs/cloud/appEngine/JiraBugFiler.java

http://findbugs.googlecode.com/
Java | 301 lines | 254 code | 38 blank | 9 comment | 44 complexity | 8b4a167fd7c4a80e0bba699453ac53ae MD5 | raw file
Possible License(s): Unlicense, GPL-2.0, LGPL-2.0, LGPL-2.1, BSD-3-Clause, Apache-2.0
  1. package edu.umd.cs.findbugs.cloud.appEngine;
  2. import java.io.IOException;
  3. import java.net.MalformedURLException;
  4. import java.net.URL;
  5. import java.rmi.RemoteException;
  6. import java.util.ArrayList;
  7. import java.util.Arrays;
  8. import java.util.List;
  9. import java.util.Map;
  10. import java.util.concurrent.ConcurrentHashMap;
  11. import java.util.logging.Level;
  12. import java.util.logging.Logger;
  13. import java.util.prefs.Preferences;
  14. import java.util.regex.Matcher;
  15. import java.util.regex.Pattern;
  16. import javax.xml.rpc.ServiceException;
  17. import com.atlassian.jira.rpc.soap.beans.RemoteComponent;
  18. import com.atlassian.jira.rpc.soap.beans.RemoteIssue;
  19. import com.atlassian.jira.rpc.soap.beans.RemoteIssueType;
  20. import com.atlassian.jira.rpc.soap.beans.RemoteProject;
  21. import com.atlassian.jira.rpc.soap.beans.RemoteStatus;
  22. import com.atlassian.jira.rpc.soap.jirasoapservice_v2.JiraSoapService;
  23. import com.atlassian.jira.rpc.soap.jirasoapservice_v2.JiraSoapServiceServiceLocator;
  24. import edu.umd.cs.findbugs.BugInstance;
  25. import edu.umd.cs.findbugs.ComponentPlugin;
  26. import edu.umd.cs.findbugs.IGuiCallback;
  27. import edu.umd.cs.findbugs.SystemProperties;
  28. import edu.umd.cs.findbugs.annotations.CheckForNull;
  29. import edu.umd.cs.findbugs.cloud.BugFiler;
  30. import edu.umd.cs.findbugs.cloud.BugFilingCommentHelper;
  31. import edu.umd.cs.findbugs.cloud.Cloud;
  32. import edu.umd.cs.findbugs.cloud.SignInCancelledException;
  33. public class JiraBugFiler implements BugFiler {
  34. private static final Logger LOGGER = Logger.getLogger(JiraBugFiler.class.getName());
  35. private static final Pattern BUG_LINK_PATTERN = Pattern.compile("(.*?)/browse/(.*-\\d+).*");
  36. private Cloud cloud;
  37. private final Map<String, JiraSession> sessionsByBaseUrl = new ConcurrentHashMap<String, JiraSession>();
  38. private String url;
  39. public JiraBugFiler(ComponentPlugin<BugFiler> plugin, Cloud cloud) {
  40. this.cloud = cloud;
  41. this.url = SystemProperties.getProperty("findbugs.jiraURL");
  42. }
  43. public URL file(BugInstance b) throws IOException, SignInCancelledException {
  44. if (url == null) {
  45. url = askUserForJiraUrl();
  46. if (url == null)
  47. return null;
  48. }
  49. String trackerUrl = processJiraDashboardUrl(url);
  50. try {
  51. return actuallyFile(b, trackerUrl);
  52. } catch (ServiceException e) {
  53. IOException ioe = new IOException(e.getMessage());
  54. ioe.initCause(e);
  55. throw ioe;
  56. }
  57. }
  58. public String getBugStatus(String bugUrl) throws ServiceException, MalformedURLException, java.rmi.RemoteException {
  59. Matcher m = BUG_LINK_PATTERN.matcher(bugUrl);
  60. if (!m.matches())
  61. return null;
  62. String baseUrl = m.group(1);
  63. String issueKey = m.group(2);
  64. JiraBugFiler.JiraSession session = getJiraSession(baseUrl);
  65. if (session == null) // maybe user cancelled signin
  66. return null;
  67. JiraSoapService service = session.service;
  68. RemoteIssue issue = service.getIssue(session.token, issueKey);
  69. RemoteStatus[] statuses = service.getStatuses(session.token);
  70. for (RemoteStatus status : statuses) {
  71. if (status.getId().equals(issue.getStatus()))
  72. return status.getName();
  73. }
  74. return null;
  75. }
  76. // ============================== end of public methods ==============================
  77. private List<String> getProjectKeys(String baseUrl) throws java.rmi.RemoteException, MalformedURLException, ServiceException {
  78. JiraSession session = getJiraSession(baseUrl);
  79. if (session == null)
  80. return null;
  81. List<String> projectKeys = new ArrayList<String>();
  82. for (RemoteProject project : session.service.getProjectsNoSchemes(session.token)) {
  83. projectKeys.add(project.getKey());
  84. }
  85. return projectKeys;
  86. }
  87. @SuppressWarnings("UnusedDeclaration")
  88. private List<String> getComponentNames(String baseUrl, String key) throws java.rmi.RemoteException, MalformedURLException,
  89. ServiceException {
  90. JiraSession session = getJiraSession(baseUrl);
  91. RemoteComponent[] components = session.service.getComponents(session.token, key);
  92. List<String> componentNames = new ArrayList<String>();
  93. for (RemoteComponent component : components) {
  94. componentNames.add(component.getName());
  95. }
  96. return componentNames;
  97. }
  98. private RemoteIssue fileBug(String baseUrl, BugInstance b, String projectKey, String componentName, String issueTypeName)
  99. throws MalformedURLException, ServiceException, java.rmi.RemoteException {
  100. JiraBugFiler.JiraSession session = getJiraSession(baseUrl);
  101. RemoteComponent actualComponent = findComponent(projectKey, componentName, session);
  102. if (actualComponent == null)
  103. throw new IllegalArgumentException("no component named " + componentName);
  104. RemoteProject project = session.service.getProjectByKey(session.token, projectKey);
  105. BugFilingCommentHelper helper = new BugFilingCommentHelper(cloud);
  106. RemoteIssue issue = new RemoteIssue();
  107. issue.setReporter(session.username);
  108. issue.setAssignee(session.username);
  109. issue.setType(getIssueType(session, issueTypeName, project));
  110. issue.setProject(projectKey);
  111. issue.setSummary(helper.getBugReportSummary(b));
  112. issue.setDescription(helper.getBugReportText(b));
  113. issue.setComponents(new RemoteComponent[] { actualComponent });
  114. return session.service.createIssue(session.token, issue);
  115. }
  116. @CheckForNull
  117. private List<String> getIssueTypes(String baseUrl) throws MalformedURLException, RemoteException, ServiceException {
  118. JiraSession session = getJiraSession(baseUrl);
  119. if (session == null)
  120. return null;
  121. List<String> typeNames = new ArrayList<String>();
  122. for (RemoteIssueType issueType : session.service.getIssueTypes(session.token)) {
  123. typeNames.add(issueType.getName());
  124. }
  125. return typeNames;
  126. }
  127. private String getIssueType(JiraSession session, String issueTypeName, RemoteProject project) throws java.rmi.RemoteException {
  128. String projectId = project.getId();
  129. String issueTypeId = null;
  130. for (RemoteIssueType issueType : session.service.getIssueTypesForProject(session.token, projectId)) {
  131. if (issueType.getName().equals(issueTypeName)) {
  132. issueTypeId = issueType.getId();
  133. break;
  134. }
  135. }
  136. if (issueTypeId == null)
  137. issueTypeId = "1";
  138. return issueTypeId;
  139. }
  140. private @CheckForNull
  141. JiraSession getJiraSession(String baseUrl) throws ServiceException, MalformedURLException, java.rmi.RemoteException {
  142. JiraBugFiler.JiraSession session = sessionsByBaseUrl.get(baseUrl);
  143. if (session != null)
  144. return session;
  145. session = new JiraSession();
  146. session.service = new JiraSoapServiceServiceLocator().getJirasoapserviceV2(new URL(baseUrl
  147. + "/rpc/soap/jirasoapservice-v2"));
  148. session.token = getToken(baseUrl, session);
  149. if (session.token == null)
  150. return null; // user cancelled login
  151. sessionsByBaseUrl.put(baseUrl, session);
  152. return session;
  153. }
  154. private String getToken(String baseUrl, JiraSession session) throws java.rmi.RemoteException {
  155. IGuiCallback callback = cloud.getBugCollection().getProject().getGuiCallback();
  156. String usernameKey = getPreferenceskeyForJiraBaseUrl(baseUrl); // alphanumeric
  157. // plus
  158. // dots
  159. // and
  160. // dashes
  161. Preferences prefs = Preferences.userNodeForPackage(JiraBugFiler.class);
  162. String lastUsername = prefs.get(usernameKey, "");
  163. List<String> results = callback.showForm(
  164. "Enter JIRA username and password for \n" + baseUrl,
  165. "JIRA",
  166. Arrays.asList(new IGuiCallback.FormItem("Username", lastUsername),
  167. new IGuiCallback.FormItem("Password").password()));
  168. if (results == null)
  169. return null;
  170. String username = results.get(0);
  171. String password = results.get(1);
  172. String token = session.service.login(username, password);
  173. session.username = username;
  174. prefs.put(usernameKey, username);
  175. return token;
  176. }
  177. private String getPreferenceskeyForJiraBaseUrl(String url) {
  178. return "jira_last_user_" + url.replaceAll("[^A-Za-z0-9_.-]", "");
  179. }
  180. private RemoteComponent findComponent(String projectKey, String componentName, JiraSession session)
  181. throws java.rmi.RemoteException {
  182. RemoteComponent[] components = session.service.getComponents(session.token, projectKey);
  183. RemoteComponent actualComponent = null;
  184. for (RemoteComponent component : components) {
  185. if (component.getName().equals(componentName)) {
  186. actualComponent = component;
  187. break;
  188. }
  189. }
  190. return actualComponent;
  191. }
  192. private URL actuallyFile(BugInstance b, final String trackerUrl) throws ServiceException, IOException, SignInCancelledException {
  193. IGuiCallback callback = cloud.getGuiCallback();
  194. List<String> issueTypes = getIssueTypes(trackerUrl);
  195. if (issueTypes == null)
  196. return null;
  197. List<String> result = callback.showForm("", "File a bug on JIRA",
  198. Arrays.asList(new IGuiCallback.FormItem("Project", null, getProjectKeys(trackerUrl)),
  199. new IGuiCallback.FormItem("Component") {
  200. private String lastProjectKey = "";
  201. public List<String> componentNames = new ArrayList<String>();
  202. @Override
  203. public List<String> getPossibleValues() {
  204. if (getItems() != null) {
  205. String newProjectKey = getItems().get(0).getCurrentValue();
  206. if (newProjectKey != null && !lastProjectKey.equals(newProjectKey)) {
  207. this.lastProjectKey = newProjectKey;
  208. try {
  209. componentNames = getComponentNames(trackerUrl, newProjectKey);
  210. } catch (Exception e) {
  211. LOGGER.log(Level.SEVERE, "Error connecting to JIRA at " + trackerUrl, e);
  212. componentNames = new ArrayList<String>();
  213. }
  214. }
  215. }
  216. return componentNames;
  217. }
  218. },
  219. new IGuiCallback.FormItem("Type", null, issueTypes)));
  220. if (result == null)
  221. return null; // user cancelled
  222. RemoteIssue issue = fileBug(trackerUrl, b, result.get(0), result.get(1), result.get(2));
  223. String bugUrl = trackerUrl + "/browse/" + issue.getKey();
  224. cloud.setBugLinkOnCloudAndStoreIssueDetails(b, bugUrl, "JIRA");
  225. return new URL(bugUrl);
  226. }
  227. private String askUserForJiraUrl() {
  228. IGuiCallback guiCallback = cloud.getBugCollection().getProject().getGuiCallback();
  229. Preferences prefs = Preferences.userNodeForPackage(JiraBugFiler.class);
  230. String lastProject = prefs.get("last_jira_url", "");
  231. String dashboardUrl = guiCallback.showQuestionDialog("Issue will be filed in JIRA.\n" + "\n"
  232. + "Type your project's JIRA dashboard URL below.\n" + "(ex. http://jira.atlassian.com/secure/Dashboard.jspa)",
  233. "JIRA", lastProject);
  234. if (dashboardUrl == null || dashboardUrl.trim().length() == 0) {
  235. return null;
  236. }
  237. dashboardUrl = processJiraDashboardUrl(dashboardUrl);
  238. prefs.put("last_jira_url", dashboardUrl);
  239. return dashboardUrl;
  240. }
  241. /**
  242. * package-private for testing
  243. */
  244. static String processJiraDashboardUrl(String dashboardUrl) {
  245. dashboardUrl = dashboardUrl.trim();
  246. Matcher m = Pattern.compile("(?:https?://)?(.*?)(?:/secure(?:/Dashboard.jspa)?.*)?").matcher(dashboardUrl);
  247. if (m.matches()) {
  248. dashboardUrl = "http://" + m.group(1);
  249. }
  250. return dashboardUrl;
  251. }
  252. private class JiraSession {
  253. public JiraSoapService service;
  254. public String token;
  255. public String username;
  256. private JiraSession() {
  257. }
  258. }
  259. public boolean ready() {
  260. // return url != null;
  261. return true;
  262. }
  263. }