View Javadoc

1   /**
2    * Copyright (C) 2008 Atlassian
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *    http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package com.atlassian.theplugin.jira.api.soap;
18  
19  import com.atlassian.theplugin.commons.configuration.ConfigurationFactory;
20  import com.atlassian.theplugin.commons.remoteapi.RemoteApiException;
21  import com.atlassian.theplugin.commons.remoteapi.RemoteApiLoginException;
22  import com.atlassian.theplugin.commons.remoteapi.ServerData;
23  import com.atlassian.theplugin.commons.remoteapi.rest.AbstractHttpSession;
24  import com.atlassian.theplugin.commons.util.HttpConfigurableAdapter;
25  import com.atlassian.theplugin.jira.api.*;
26  import com.atlassian.theplugin.jira.api.soap.axis.*;
27  import com.atlassian.theplugin.jira.model.JIRAServerCache;
28  import com.atlassian.theplugin.util.PluginUtil;
29  import com.intellij.openapi.diagnostic.Logger;
30  import org.apache.axis.AxisProperties;
31  import org.xml.sax.SAXException;
32  
33  import javax.xml.rpc.ServiceException;
34  import java.net.MalformedURLException;
35  import java.net.URL;
36  import java.rmi.RemoteException;
37  import java.util.ArrayList;
38  import java.util.Calendar;
39  import java.util.List;
40  import java.util.concurrent.CopyOnWriteArrayList;
41  
42  public class JIRASessionImpl implements JIRASession {
43  
44  	private String token;
45  	private JiraSoapService service;
46  	private ServerData server;
47  	private URL portAddress;
48  
49  	public static final int ONE_DAY_AGO = -24;
50  
51  	private boolean loggedIn;
52  
53  	//
54  	// AxisProperties are shit - if you try to set nonexistent property to null, NPE is thrown. Moreover, sometimes
55  	// setting apparently *existing* property to null also throws NPE (see bug PL-412)! Crap, crap, crap...
56  	//
57  	private void setAxisProperty(String name, String value) {
58  		if (value == null) {
59  			if (AxisProperties.getProperty(name) != null) {
60  				try {
61  					AxisProperties.setProperty(name, "");
62  				} catch (NullPointerException e) {
63  					Logger.getInstance(getClass().getName()).info("Setting AXIS property " + name + " to empty", e);
64  				}
65  			}
66  		} else {
67  			AxisProperties.setProperty(name, value);
68  		}
69  	}
70  
71  	private void setSystemProperty(String name, String value) {
72  
73  		if (value == null) {
74  			//if (System.getProperty(name) != null) {
75  			try {
76  				System.setProperty(name, "");
77  			} catch (NullPointerException e) {
78  				Logger.getInstance(getClass().getName()).info("Setting system property " + name + " to empty", e);
79  			}
80  			//}
81  		} else {
82  			System.setProperty(name, value);
83  		}
84  	}
85  
86  	private void setProxy() {
87  		boolean useIdeaProxySettings =
88  				ConfigurationFactory.getConfiguration().getGeneralConfigurationData().getUseIdeaProxySettings();
89  		HttpConfigurableAdapter proxyInfo = ConfigurationFactory.getConfiguration().transientGetHttpConfigurable();
90  		String host = null;
91  		String port = null;
92  		String user = null;
93  		String password = null;
94  		if (useIdeaProxySettings && proxyInfo.isUseHttpProxy()) {
95  			host = proxyInfo.getProxyHost();
96  			port = String.valueOf(proxyInfo.getProxyPort());
97  			if (proxyInfo.isProxyAuthentication()) {
98  				user = proxyInfo.getProxyLogin();
99  				password = proxyInfo.getPlainProxyPassword();
100 			}
101 		}
102 
103 		//
104 		// well - re-setting proxy does not really work - Axis bug
105 		// see: http://issues.apache.org/jira/browse/AXIS-2295
106 		// So in order to apply new proxy settings, IDEA has to be restarted
107 		// all software sucks
108 		//
109 		setAxisProperty("http.proxyHost", host);
110 		setAxisProperty("http.proxyPort", port);
111 
112 		setSystemProperty("http.proxyHost", host);
113 		setSystemProperty("http.proxyPort", port);
114 
115 		setAxisProperty("http.proxyUser", user);
116 		setSystemProperty("http.proxyUser", user);
117 
118 		setAxisProperty("http.proxyPassword", password);
119 		setSystemProperty("http.proxyPassword", password);
120 
121 	}
122 
123 	public JIRASessionImpl(ServerData server) throws ServiceException, MalformedURLException {
124 		portAddress = new URL(server.getUrl() + "/rpc/soap/jirasoapservice-v2");
125 		JiraSoapServiceServiceLocator loc = new JiraSoapServiceServiceLocator();
126 		AbstractHttpSession.setUrl(portAddress); // dirty hack
127 		service = loc.getJirasoapserviceV2(portAddress);
128 		setProxy();
129 
130 		this.server = server;
131 	}
132 
133 	public void login(String userName, String password) throws RemoteApiException {
134 		try {
135 			token = service.login(userName, password);
136 		} catch (RemoteAuthenticationException e) {
137 			throw new RemoteApiLoginException("Authentication failed");
138 		} catch (RemoteException e) {
139 			throw new RemoteApiException(e.toString());
140 		}
141 		loggedIn = true;
142 	}
143 
144 	public void logout() {
145 		try {
146 			if (service.logout(token)) {
147 				token = null;
148 				loggedIn = false;
149 			}
150 		} catch (java.rmi.RemoteException e) {
151 			// todo: log the exception
152 		}
153 	}
154 
155 	public void logWork(JIRAIssue issue, String timeSpent, Calendar startDate, String comment,
156 			boolean updateEstimate, String newEstimate)
157 			throws RemoteApiException {
158 		RemoteWorklog workLog = new RemoteWorklog();
159 		workLog.setStartDate(startDate);
160 		workLog.setTimeSpent(timeSpent);
161 		if (comment != null) {
162 			workLog.setComment(comment);
163 		}
164 		try {
165 			if (updateEstimate) {
166 				if (newEstimate != null) {
167 					service.addWorklogWithNewRemainingEstimate(token, issue.getKey(), workLog, newEstimate);
168 				} else {
169 					service.addWorklogAndAutoAdjustRemainingEstimate(token, issue.getKey(), workLog);
170 				}
171 			} else {
172 				service.addWorklogAndRetainRemainingEstimate(token, issue.getKey(), workLog);
173 			}
174 		} catch (RemoteException e) {
175 			throw new RemoteApiException(e.toString(), e);
176 		}
177 	}
178 
179 	public JIRAIssue createIssue(JIRAIssue issue) throws RemoteApiException {
180 		RemoteIssue remoteIssue = new RemoteIssue();
181 
182 		remoteIssue.setProject(issue.getProjectKey());
183 		remoteIssue.setType(String.valueOf(issue.getTypeConstant().getId()));
184 		remoteIssue.setSummary(issue.getSummary());
185 		if (issue.getPriorityConstant().getId() != JIRAServerCache.ANY_ID) {
186 			remoteIssue.setPriority(String.valueOf(issue.getPriorityConstant().getId()));
187 		}
188 
189 		if (issue.getDescription() != null) {
190 			remoteIssue.setDescription(issue.getDescription());
191 		}
192 		if (issue.getAssignee() != null) {
193 			remoteIssue.setAssignee(issue.getAssignee());
194 		}
195 
196 		final List<JIRAConstant> components = issue.getComponents();
197 		if (components != null && components.size() > 0) {
198 			RemoteComponent[] remoteComponents = new RemoteComponent[components.size()];
199 			int i = 0;
200 			for (JIRAConstant component : components) {
201 				remoteComponents[i] = new RemoteComponent(String.valueOf(component.getId()), component.getName());
202 				i++;
203 			}
204 			remoteIssue.setComponents(remoteComponents);
205 		}
206 
207         final List<JIRAConstant> versions = issue.getAffectsVersions();
208         if (versions != null && versions.size() > 0) {
209             RemoteVersion[] remoteVersions = new RemoteVersion[versions.size()];
210             int i = 0;
211             for (JIRAConstant version : versions) {
212                 remoteVersions[i] = new RemoteVersion();
213                 remoteVersions[i].setId(String.valueOf(version.getId()));
214                 ++i;
215             }
216             remoteIssue.setAffectsVersions(remoteVersions);
217         }
218 
219 		try {
220 			remoteIssue = service.createIssue(token, remoteIssue);
221 		} catch (RemoteException e) {
222 			throw new RemoteApiException(e.toString(), e);
223 		}
224 
225 		// todo: fill in all other fields. For now only the issue key and URL is being displayed
226 		JIRAIssueBean retVal = new JIRAIssueBean(server);
227 		retVal.setKey(remoteIssue.getKey());
228 		return retVal;
229 	}
230 
231 	public JIRAIssue getIssueDetails(JIRAIssue issue) throws RemoteApiException {
232 		RemoteSecurityLevel securityLevel = null;
233 
234 		try {
235 			securityLevel = service.getSecurityLevel(token, issue.getKey());
236 		} catch (RemoteException e) {
237 			PluginUtil.getLogger().warn(
238 					"Soap method 'getSecurityLevel' thrown exception. "
239 							+ "Probably there is no 'SecurityLevel' on JIRA (non enterprise version of JIRA).", e);
240 		} catch (ClassCastException e) {
241 			PluginUtil.getLogger().warn(
242                     "Soap method 'getSecurityLevel' thrown ClassCastException. Probably some JIRA error.", e);
243 		} catch (Exception e) {
244             // PL-1492
245             if (e instanceof SAXException) {
246                 PluginUtil.getLogger().warn(
247                         "Soap method 'getSecurityLevel' thrown SAXException. Probably some JIRA error.", e);
248             } else {
249                 throw new RemoteApiException(e);
250             }
251         }
252 
253 		try {
254 			RemoteIssue rIssue = service.getIssue(token, issue.getKey());
255 
256 
257 			if (rIssue == null) {
258 				throw new RemoteApiException("Unable to retrieve issue details");
259 			}
260 			JIRAIssueBean issueBean = new JIRAIssueBean(issue);
261 
262 			if (securityLevel != null) {
263 				issueBean.setSecurityLevel(
264 						new JIRASecurityLevelBean(Long.valueOf(securityLevel.getId()), securityLevel.getName()));
265 			}
266 
267 			RemoteVersion[] aVers = rIssue.getAffectsVersions();
268 			List<JIRAConstant> av = new ArrayList<JIRAConstant>();
269 			for (RemoteVersion v : aVers) {
270 				av.add(new JIRAVersionBean(Long.valueOf(v.getId()), v.getName()));
271 			}
272 			issueBean.setAffectsVersions(av);
273 
274 			RemoteVersion[] fVers = rIssue.getFixVersions();
275 			List<JIRAConstant> fv = new ArrayList<JIRAConstant>();
276 			for (RemoteVersion v : fVers) {
277 				fv.add(new JIRAVersionBean(Long.valueOf(v.getId()), v.getName()));
278 			}
279 			issueBean.setFixVersions(fv);
280 
281 			RemoteComponent[] comps = rIssue.getComponents();
282 			List<JIRAConstant> c = new ArrayList<JIRAConstant>();
283 			for (RemoteComponent rc : comps) {
284 				c.add(new JIRAComponentBean(Long.valueOf(rc.getId()), rc.getName()));
285 			}
286 			issueBean.setComponents(c);
287 
288 			issueBean.setProjectKey(rIssue.getProject());
289 			issueBean.setSummary(rIssue.getSummary());
290 
291 			issueBean.setRawSoapIssue(rIssue);
292 
293 			return issueBean;
294 
295 		} catch (RemoteException e) {
296 			throw new RemoteApiException(e.toString(), e);
297 		} catch (ClassCastException e) {
298 			throw new RemoteApiException(e.toString(), e);
299 		}
300 
301 	}
302 
303 	public void addComment(String issueKey, String comment) throws RemoteApiException {
304 		try {
305 			RemoteComment rComment = new RemoteComment();
306 			rComment.setBody(comment);
307 			service.addComment(token, issueKey, rComment);
308 		} catch (RemoteException e) {
309 			throw new RemoteApiException(e.toString(), e);
310 		}
311 	}
312 
313 	public List<JIRAProject> getProjects() throws RemoteApiException {
314 		try {
315 			RemoteProject[] projects = service.getProjectsNoSchemes(token);
316 			List<JIRAProject> projectList = new ArrayList<JIRAProject>(projects.length);
317 
318 			for (RemoteProject p : projects) {
319 				JIRAProjectBean project = new JIRAProjectBean();
320 
321 				project.setName(p.getName());
322 				project.setKey(p.getKey());
323 				project.setDescription(p.getDescription());
324 				project.setUrl(p.getUrl());
325 				project.setLead(p.getLead());
326 				project.setId(Long.valueOf(p.getId()));
327 
328 				projectList.add(project);
329 			}
330 
331 			return projectList;
332 		} catch (RemoteException e) {
333 			throw new RemoteApiException(e.toString(), e);
334 		}
335 	}
336 
337 	private List<JIRAConstant> issueTableToList(RemoteIssueType[] types) throws MalformedURLException {
338 		List<JIRAConstant> typesList = new ArrayList<JIRAConstant>();
339 		for (RemoteIssueType type : types) {
340 			typesList.add(new JIRAIssueTypeBean(Long.valueOf(type.getId()), type.getName(), new URL(type.getIcon())));
341 		}
342 		return typesList;
343 	}
344 
345 	public List<JIRAConstant> getIssueTypes() throws RemoteApiException {
346 		try {
347 			return issueTableToList(service.getIssueTypes(token));
348 		} catch (RemoteException e) {
349 			throw new RemoteApiException(e.toString(), e);
350 		} catch (MalformedURLException e) {
351 			throw new RemoteApiException(e.toString(), e);
352 		}
353 
354 	}
355 
356 	public List<JIRAConstant> getIssueTypesForProject(String project) throws RemoteApiException {
357 		try {
358 			return issueTableToList(service.getIssueTypesForProject(token, project));
359 		} catch (RemoteException e) {
360 			throw new RemoteApiException(e.toString(), e);
361 		} catch (MalformedURLException e) {
362 			throw new RemoteApiException(e.toString(), e);
363 		}
364 	}
365 
366 	public List<JIRAConstant> getSubtaskIssueTypes() throws RemoteApiException {
367 		try {
368 			return issueTableToList(service.getSubTaskIssueTypes(token));
369 		} catch (RemoteException e) {
370 			throw new RemoteApiException(e.toString(), e);
371 		} catch (MalformedURLException e) {
372 			throw new RemoteApiException(e.toString(), e);
373 		}
374 
375 	}
376 
377 	public List<JIRAConstant> getSubtaskIssueTypesForProject(String project) throws RemoteApiException {
378 		try {
379 			return issueTableToList(service.getSubTaskIssueTypesForProject(token, project));
380 		} catch (RemoteException e) {
381 			throw new RemoteApiException(e.toString(), e);
382 		} catch (MalformedURLException e) {
383 			throw new RemoteApiException(e.toString(), e);
384 		}
385 	}
386 
387 	public List<JIRAConstant> getStatuses() throws RemoteApiException {
388 		try {
389 			RemoteStatus[] statuses = service.getStatuses(token);
390 
391 			List<JIRAConstant> statusesList = new ArrayList<JIRAConstant>(statuses.length);
392 			for (RemoteStatus status : statuses) {
393 				statusesList.add(new JIRAStatusBean(
394 						Long.valueOf(status.getId()), status.getName(), new URL(status.getIcon())));
395 			}
396 			return statusesList;
397 		} catch (RemoteException e) {
398 			throw new RemoteApiException(e.toString(), e);
399 		} catch (MalformedURLException e) {
400 			throw new RemoteApiException(e.toString(), e);
401 		}
402 	}
403 
404 	public List<JIRAComponentBean> getComponents(String projectKey) throws RemoteApiException {
405 		try {
406 			RemoteComponent[] components = service.getComponents(token, projectKey);
407 
408 			List<JIRAComponentBean> componentsList = new ArrayList<JIRAComponentBean>(components.length);
409 			for (RemoteComponent c : components) {
410 				componentsList.add(new JIRAComponentBean(Long.valueOf(c.getId()), c.getName()));
411 			}
412 			return componentsList;
413 		} catch (RemoteException e) {
414 			throw new RemoteApiException(e.toString(), e);
415 		}
416 	}
417 
418 	public List<JIRAVersionBean> getVersions(String projectKey) throws RemoteApiException {
419 		try {
420 			RemoteVersion[] versions = service.getVersions(token, projectKey);
421 
422 			List<JIRAVersionBean> versionsList = new ArrayList<JIRAVersionBean>(versions.length);
423 			for (RemoteVersion v : versions) {
424 				versionsList.add(new JIRAVersionBean(Long.valueOf(v.getId()), v.getName()));
425 			}
426 			return versionsList;
427 		} catch (RemoteException e) {
428 			throw new RemoteApiException(e.toString(), e);
429 		}
430 	}
431 
432 	public List<JIRAConstant> getPriorities() throws RemoteApiException {
433 		try {
434 			RemotePriority[] priorities = service.getPriorities(token);
435 
436 			List<JIRAConstant> prioritiesList = new ArrayList<JIRAConstant>(priorities.length);
437 			for (RemotePriority p : priorities) {
438 				prioritiesList.add(new JIRAPriorityBean(Long.valueOf(p.getId()), p.getName(), new URL(p.getIcon())));
439 			}
440 			return prioritiesList;
441 		} catch (RemoteException e) {
442 			throw new RemoteApiException(e.toString(), e);
443 		} catch (MalformedURLException e) {
444 			throw new RemoteApiException(e.toString(), e);
445 		}
446 	}
447 
448 	public List<JIRAResolutionBean> getResolutions() throws RemoteApiException {
449 		try {
450 			RemoteResolution[] resolutions = service.getResolutions(token);
451 
452 			List<JIRAResolutionBean> resolutionsList = new ArrayList<JIRAResolutionBean>(resolutions.length);
453 			for (RemoteResolution p : resolutions) {
454 				resolutionsList.add(new JIRAResolutionBean(Long.valueOf(p.getId()), p.getName()));
455 			}
456 			return resolutionsList;
457 		} catch (RemoteException e) {
458 			throw new RemoteApiException(e.toString(), e);
459 		}
460 	}
461 
462 	public List<JIRAQueryFragment> getSavedFilters() throws RemoteApiException {
463 		try {
464 			RemoteFilter[] filters = service.getSavedFilters(token);
465 
466 			List<JIRAQueryFragment> filtersList = new ArrayList<JIRAQueryFragment>(filters.length);
467 			for (RemoteFilter f : filters) {
468 				filtersList.add(new JIRASavedFilterBean(f.getName(), Long.valueOf(f.getId())));
469 			}
470 			return filtersList;
471 		} catch (RemoteException e) {
472 			throw new RemoteApiException(e.toString(), e);
473 		}
474 
475 	}
476 
477 	public void setAssignee(JIRAIssue issue, String assignee) throws RemoteApiException {
478 		RemoteFieldValue v = new RemoteFieldValue();
479 		RemoteFieldValue[] vTable = {v};
480 		v.setId("assignee");
481 		v.setValues(new String[]{assignee});
482 		try {
483 			service.updateIssue(token, issue.getKey(), vTable);
484 		} catch (RemoteException e) {
485 			throw new RemoteApiException(e.toString(), e);
486 		}
487 	}
488 
489 	public List<JIRAAction> getAvailableActions(JIRAIssue issue) throws RemoteApiException {
490 		try {
491 			RemoteNamedObject[] actions = service.getAvailableActions(token, issue.getKey());
492 			List<JIRAAction> actionList = new ArrayList<JIRAAction>(actions != null ? actions.length : 0);
493 			if (actions != null) {
494 				for (RemoteNamedObject action : actions) {
495 					actionList.add(new JIRAActionBean(Long.valueOf(action.getId()), action.getName()));
496 				}
497 			}
498 			return actionList;
499 		} catch (RemoteException e) {
500 			throw new RemoteApiException(e.toString(), e);
501 		} catch (ClassCastException e) {
502 			throw new RemoteApiException(e.toString(), e);
503 		}
504 	}
505 
506 	public List<JIRAActionField> getFieldsForAction(JIRAIssue issue, JIRAAction action) throws RemoteApiException {
507 		try {
508 			RemoteField[] fields = service.getFieldsForAction(
509 					token, issue.getKey(), Long.valueOf(action.getId()).toString());
510 			List<JIRAActionField> fieldList = new ArrayList<JIRAActionField>(fields.length);
511 			for (RemoteField f : fields) {
512 				fieldList.add(new JIRAActionFieldBean(f.getId(), f.getName()));
513 			}
514 			return fieldList;
515 		} catch (RemoteException e) {
516 			throw new RemoteApiException(e.toString(), e);
517 		}
518 	}
519 
520 	public void progressWorkflowAction(JIRAIssue issue, JIRAAction action, List<JIRAActionField> fields)
521 			throws RemoteApiException {
522 		try {
523 			if (fields == null) {
524 				RemoteFieldValue[] dummyValues = new RemoteFieldValue[0];
525 				service.progressWorkflowAction(token, issue.getKey(), String.valueOf(action.getId()), dummyValues);
526 			} else {
527 
528 				CopyOnWriteArrayList<JIRAActionField> safeFields = new CopyOnWriteArrayList<JIRAActionField>(fields);
529 
530 				for (JIRAActionField field : safeFields) {
531 					if (field.getValues() == null) {
532 						safeFields.remove(field);
533 					}
534 				}
535 
536 				int i = 0;
537 				RemoteFieldValue[] values = new RemoteFieldValue[safeFields.size()];
538 
539 				for (JIRAActionField field : safeFields) {
540 					List<String> fieldValues = field.getValues();
541 					String[] fieldValueTable = fieldValues.toArray(new String[fieldValues.size()]);
542 					values[i] = new RemoteFieldValue(field.getFieldId(), fieldValueTable);
543 					++i;
544 				}
545 				service.progressWorkflowAction(token, issue.getKey(), String.valueOf(action.getId()), values);
546 			}
547 		} catch (RemoteException e) {
548 			throw new RemoteApiException(e.toString(), e);
549 		}
550 	}
551 
552 	public List<JIRAComment> getComments(JIRAIssue issue) throws RemoteApiException {
553 		try {
554 			RemoteComment[] comments = service.getComments(token, issue.getKey());
555 			if (comments == null) {
556 				throw new RemoteApiException("Unable to retrieve comments");
557 			}
558 
559 			List<JIRAComment> commentsList = new ArrayList<JIRAComment>(comments.length);
560 			for (RemoteComment c : comments) {
561 				commentsList.add(new JIRACommentBean(c.getId(), c.getAuthor(), c.getBody(), c.getCreated()));
562 			}
563 			return commentsList;
564 		} catch (RemoteException e) {
565 			throw new RemoteApiException(e.toString(), e);
566 		}
567 	}
568 
569 	public JIRAUserBean getUser(String loginName) throws RemoteApiException, JiraUserNotFoundException {
570 		try {
571 			RemoteUser ru = service.getUser(token, loginName);
572 			if (ru == null) {
573 				throw new JiraUserNotFoundException("User Name for " + loginName + " not found");
574 			}
575 			return new JIRAUserBean(-1, ru.getFullname(), ru.getName()) {
576 				@Override
577 				public String getQueryStringFragment() {
578 					return null;
579 				}
580 
581 				public JIRAQueryFragment getClone() {
582 					return null;
583 				}
584 			};
585 		} catch (RemoteException e) {
586 			throw new RemoteApiException(e.toString(), e);
587 		} catch (ClassCastException e) {
588 			throw new RemoteApiException(e.toString(), e);
589 		}
590 	}
591 
592 	public boolean isLoggedIn() {
593 		return loggedIn;
594 	}
595 }