diff --git a/src/main/java/pl/com/it_crowd/youtrack/api/YoutrackAPI.java b/src/main/java/pl/com/it_crowd/youtrack/api/YoutrackAPI.java index cf279b7..cf36a54 100644 --- a/src/main/java/pl/com/it_crowd/youtrack/api/YoutrackAPI.java +++ b/src/main/java/pl/com/it_crowd/youtrack/api/YoutrackAPI.java @@ -11,6 +11,7 @@ import org.apache.http.client.ClientProtocolException; import org.apache.http.client.HttpClient; import org.apache.http.client.HttpResponseException; import org.apache.http.client.entity.UrlEncodedFormEntity; +import org.apache.http.client.methods.HttpDelete; import org.apache.http.client.methods.HttpEntityEnclosingRequestBase; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; @@ -62,343 +63,354 @@ import java.util.regex.Pattern; public class YoutrackAPI { // ------------------------------ FIELDS ------------------------------ - private final static QName Enumeration_QNAME = new QName("", "enumeration"); + private final static QName Enumeration_QNAME = new QName("", "enumeration"); - private final static QName Issue_QNAME = new QName("", "issue"); + private final static QName Issue_QNAME = new QName("", "issue"); - private HttpClient httpClient; + private HttpClient httpClient; - private String serviceLocation; + private String serviceLocation; // -------------------------- STATIC METHODS -------------------------- - private static HttpClient getDefaultHttpClient() - { - SSLContext sslContext; - try { - sslContext = SSLContext.getInstance("SSL"); - } catch (NoSuchAlgorithmException e) { - throw new RuntimeException(e); - } - // set up a TrustManager that trusts everything - try { - sslContext.init(null, new TrustManager[]{new X509TrustManager() { - public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException - { - } - - public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException - { - } - - public X509Certificate[] getAcceptedIssuers() - { - return new X509Certificate[0]; - } - }}, new SecureRandom()); - } catch (KeyManagementException e) { - throw new RuntimeException(e); + private static HttpClient getDefaultHttpClient() + { + SSLContext sslContext; + try { + sslContext = SSLContext.getInstance("SSL"); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException(e); + } + // set up a TrustManager that trusts everything + try { + sslContext.init(null, new TrustManager[]{new X509TrustManager() { + public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException + { } - SSLSocketFactory sf = new SSLSocketFactory(sslContext); - Scheme httpsScheme = new Scheme("https", 443, sf); - SchemeRegistry schemeRegistry = SchemeRegistryFactory.createDefault(); - schemeRegistry.register(schemeRegistry.unregister("https")); - schemeRegistry.register(httpsScheme); + public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException + { + } - ClientConnectionManager cm = new PoolingClientConnectionManager(schemeRegistry); - return new DefaultHttpClient(cm); + public X509Certificate[] getAcceptedIssuers() + { + return new X509Certificate[0]; + } + }}, new SecureRandom()); + } catch (KeyManagementException e) { + throw new RuntimeException(e); } - private static boolean isBlank(String str) - { - int strLen; - if (str == null || (strLen = str.length()) == 0) { - return true; - } - for (int i = 0; i < strLen; i++) { - if ((!Character.isWhitespace(str.charAt(i)))) { - return false; - } - } - return true; + SSLSocketFactory sf = new SSLSocketFactory(sslContext); + Scheme httpsScheme = new Scheme("https", 443, sf); + SchemeRegistry schemeRegistry = SchemeRegistryFactory.createDefault(); + schemeRegistry.register(schemeRegistry.unregister("https")); + schemeRegistry.register(httpsScheme); + + ClientConnectionManager cm = new PoolingClientConnectionManager(schemeRegistry); + return new DefaultHttpClient(cm); + } + + private static boolean isBlank(String str) + { + int strLen; + if (str == null || (strLen = str.length()) == 0) { + return true; + } + for (int i = 0; i < strLen; i++) { + if ((!Character.isWhitespace(str.charAt(i)))) { + return false; + } } + return true; + } // --------------------------- CONSTRUCTORS --------------------------- - public YoutrackAPI(String serviceLocation) - { - this(serviceLocation, null); - } + public YoutrackAPI(String serviceLocation) + { + this(serviceLocation, null); + } - public YoutrackAPI(String serviceLocation, HttpClient httpClient) - { - this.serviceLocation = serviceLocation; - this.httpClient = httpClient == null ? getDefaultHttpClient() : httpClient; - } + public YoutrackAPI(String serviceLocation, HttpClient httpClient) + { + this.serviceLocation = serviceLocation; + this.httpClient = httpClient == null ? getDefaultHttpClient() : httpClient; + } - public YoutrackAPI(String serviceLocation, String username, String password) throws IOException, JAXBException - { - this(serviceLocation, null, username, password); - } + public YoutrackAPI(String serviceLocation, String username, String password) throws IOException, JAXBException + { + this(serviceLocation, null, username, password); + } - public YoutrackAPI(String serviceLocation, HttpClient httpClient, String username, String password) throws IOException, JAXBException - { - this(serviceLocation, httpClient); - login(username, password); - } + public YoutrackAPI(String serviceLocation, HttpClient httpClient, String username, String password) throws IOException, JAXBException + { + this(serviceLocation, httpClient); + login(username, password); + } // --------------------- GETTER / SETTER METHODS --------------------- - public String getServiceLocation() - { - return serviceLocation; - } + public String getServiceLocation() + { + return serviceLocation; + } // -------------------------- OTHER METHODS -------------------------- - public void command(String issueId, String command) throws IOException - { - command(issueId, command, null, null, null, null); + public void command(String issueId, String command) throws IOException + { + command(issueId, command, null, null, null, null); + } + + public void command(String issueId, Command command) throws IOException + { + command(issueId, command.toString()); + } + + public void command(String issueId, String command, String comment, String group, Boolean disableNotifications, String runAs) throws IOException + { + final HttpPost request = new HttpPost(serviceLocation + "/rest/issue/" + issueId + "/execute"); + final List parameters = new ArrayList(); + parameters.add(new BasicNameValuePair("command", command)); + if (!isBlank(comment)) { + parameters.add(new BasicNameValuePair("comment", comment)); } - - public void command(String issueId, Command command) throws IOException - { - command(issueId, command.toString()); + if (!isBlank(group)) { + parameters.add(new BasicNameValuePair("group", group)); } - - public void command(String issueId, String command, String comment, String group, Boolean disableNotifications, String runAs) throws IOException - { - final HttpPost request = new HttpPost(serviceLocation + "/rest/issue/" + issueId + "/execute"); - final List parameters = new ArrayList(); - parameters.add(new BasicNameValuePair("command", command)); - if (!isBlank(comment)) { - parameters.add(new BasicNameValuePair("comment", comment)); - } - if (!isBlank(group)) { - parameters.add(new BasicNameValuePair("group", group)); - } - if (disableNotifications != null) { - parameters.add(new BasicNameValuePair("disableNotifications", disableNotifications.toString())); - } - if (!isBlank(runAs)) { - parameters.add(new BasicNameValuePair("runAs", runAs)); - } - request.setEntity(new UrlEncodedFormEntity(parameters)); - execute(request); + if (disableNotifications != null) { + parameters.add(new BasicNameValuePair("disableNotifications", disableNotifications.toString())); } - - /** - * Creates new issue on Youtrack. - * - * @param project project to create issue in - * @param summary summary of the issue - * @param description longer description of the issue - * - * @return issue id of created issue - * - * @throws IOException in case of communication problems - */ - public String createIssue(String project, String summary, String description) throws IOException - { - final URI uri; - try { - uri = new URIBuilder(serviceLocation + "/rest/issue").build(); - } catch (URISyntaxException e) { - throw new RuntimeException(e); - } - final HttpPut request = createPutRequest(uri, new BasicNameValuePair("project", project), new BasicNameValuePair("summary", summary), - new BasicNameValuePair("description", description)); - final HttpResponse response = httpClient.execute(request); - final StatusLine statusLine = response.getStatusLine(); - final HttpEntity entity = response.getEntity(); - final String responseText = entity == null ? null : EntityUtils.toString(entity); - throwExceptionsIfNeeded(statusLine, responseText); - - final Header header = response.getFirstHeader(HttpHeaders.LOCATION); - if (header == null) { - throw new YoutrackAPIException("Missing location header despite positive status code: " + statusLine.getStatusCode()); - } - final String issueURL = header.getValue(); - final Matcher matcher = Pattern.compile(".*(" + project + "-\\d+)").matcher(issueURL); - if (!matcher.find() || matcher.groupCount() < 1) { - throw new YoutrackAPIException("Cannot extract issue id from issue URL: " + issueURL); - } - return matcher.group(1); + if (!isBlank(runAs)) { + parameters.add(new BasicNameValuePair("runAs", runAs)); } - - public AssigneeList getAssignees(String project) throws IOException, JAXBException - { - final URI uri; - try { - uri = new URIBuilder(serviceLocation + "/rest/admin/project/" + project + "/assignee").build(); - } catch (URISyntaxException e) { - throw new RuntimeException(e); - } - final String responseString = execute(new HttpGet(uri)); - final Object result = YoutrackUnmarshaller.unmarshall(responseString); - if (result instanceof AssigneeList) { - return (AssigneeList) result; - } else { - throw new YoutrackAPIException("Unexpected type: " + result); - } + request.setEntity(new UrlEncodedFormEntity(parameters)); + execute(request); + } + + /** + * Creates new issue on Youtrack. + * + * @param project project to create issue in + * @param summary summary of the issue + * @param description longer description of the issue + * + * @return issue id of created issue + * + * @throws IOException in case of communication problems + */ + public String createIssue(String project, String summary, String description) throws IOException + { + final URI uri; + try { + uri = new URIBuilder(serviceLocation + "/rest/issue").build(); + } catch (URISyntaxException e) { + throw new RuntimeException(e); } - - public Enumeration getBundle(String customField) throws IOException, JAXBException - { - final URI uri; - try { - uri = new URIBuilder(serviceLocation + "/rest/admin/customfield/bundle/" + customField).build(); - } catch (URISyntaxException e) { - throw new RuntimeException(e); - } - final Object result = YoutrackUnmarshaller.unmarshall(execute(new HttpGet(uri))); - if (result instanceof Enumeration) { - return (Enumeration) result; - } else if (result instanceof JAXBElement) { - final JAXBElement jaxbElement = (JAXBElement) result; - if (Enumeration_QNAME.equals(jaxbElement.getName())) { - return (Enumeration) ((JAXBElement) result).getValue(); - } else { - throw new YoutrackAPIException("Unexpected type: " + jaxbElement.getValue()); - } - } else { - throw new YoutrackAPIException("Unexpected type: " + result); - } + final HttpPut request = createPutRequest(uri, new BasicNameValuePair("project", project), new BasicNameValuePair("summary", summary), + new BasicNameValuePair("description", description)); + final HttpResponse response = httpClient.execute(request); + final StatusLine statusLine = response.getStatusLine(); + final HttpEntity entity = response.getEntity(); + final String responseText = entity == null ? null : EntityUtils.toString(entity); + throwExceptionsIfNeeded(statusLine, responseText); + + final Header header = response.getFirstHeader(HttpHeaders.LOCATION); + if (header == null) { + throw new YoutrackAPIException("Missing location header despite positive status code: " + statusLine.getStatusCode()); } - - public List getIndividualAssignees(String project) throws IOException, JAXBException - { - final URI uri; - try { - uri = new URIBuilder(serviceLocation + "/rest/admin/project/" + project + "/assignee/individual").build(); - } catch (URISyntaxException e) { - throw new RuntimeException(e); - } - final String responseString = execute(new HttpGet(uri)); - final Object result = YoutrackUnmarshaller.unmarshall(responseString); - if (result instanceof UserRefs) { - return ((UserRefs) result).getUsers(); - } else { - throw new YoutrackAPIException("Unexpected type: " + result); - } + final String issueURL = header.getValue(); + final Matcher matcher = Pattern.compile(".*(" + project + "-\\d+)").matcher(issueURL); + if (!matcher.find() || matcher.groupCount() < 1) { + throw new YoutrackAPIException("Cannot extract issue id from issue URL: " + issueURL); } - - public IssueWrapper getIssue(String issueId) throws IOException, JAXBException - { - final URI uri; - try { - uri = new URIBuilder(serviceLocation + "/rest/issue/" + issueId).build(); - } catch (URISyntaxException e) { - throw new RuntimeException(e); - } - final String responseString; - try { - responseString = execute(new HttpGet(uri)); - } catch (YoutrackErrorException e) { - if (e.getStatusCode() == HttpStatus.SC_NOT_FOUND) { - throw new NoResultFoundException(e.getMessage(), e); - } else { - throw e; - } - } - final Object result = YoutrackUnmarshaller.unmarshall(responseString); - if (result instanceof pl.com.it_crowd.youtrack.api.rest.Issue) { - return new IssueWrapper((Issue) result); - } else if (result instanceof JAXBElement) { - final JAXBElement jaxbElement = (JAXBElement) result; - if (Issue_QNAME.equals(jaxbElement.getName())) { - return new IssueWrapper((Issue) jaxbElement.getValue()); - } else { - throw new YoutrackAPIException("Unexpected type: " + jaxbElement.getValue()); - } - } else { - throw new YoutrackAPIException("Unexpected type " + result); - } + return matcher.group(1); + } + + public void deleteIssue(String issueId) throws IOException + { + final URI uri; + try { + uri = new URIBuilder(serviceLocation + "/rest/issue/" + issueId).build(); + } catch (URISyntaxException e) { + throw new RuntimeException(e); } - - public void login(String username, String password) throws IOException, JAXBException - { - final HttpPost request = new HttpPost(serviceLocation + "/rest/user/login"); - request.setEntity(new UrlEncodedFormEntity(Arrays.asList(new BasicNameValuePair("login", username), new BasicNameValuePair("password", password)))); - execute(request); + execute(new HttpDelete(uri)); + } + + public AssigneeList getAssignees(String project) throws IOException, JAXBException + { + final URI uri; + try { + uri = new URIBuilder(serviceLocation + "/rest/admin/project/" + project + "/assignee").build(); + } catch (URISyntaxException e) { + throw new RuntimeException(e); } - - public List searchIssuesByProject(String project, Object filter) throws JAXBException, IOException - { - final String url = serviceLocation + "/rest/issue/byproject/" + project + "?filter=" + (filter == null ? "" : filter); - final Object result = YoutrackUnmarshaller.unmarshall(execute(new HttpGet(url))); - if (!(result instanceof Issues)) { - throw new YoutrackAPIException("Unmarshalling problem. Expected Issues, received: " + result.getClass() + " " + result); - } - List issues = ((Issues) result).getIssues(); - List wrappedIssues = new ArrayList(); - for (Issue issue : issues) { - wrappedIssues.add(new IssueWrapper(issue)); - } - return wrappedIssues; + final String responseString = execute(new HttpGet(uri)); + final Object result = YoutrackUnmarshaller.unmarshall(responseString); + if (result instanceof AssigneeList) { + return (AssigneeList) result; + } else { + throw new YoutrackAPIException("Unexpected type: " + result); } - - public void updateIssue(String issueId, String summary, String description) throws IOException - { - final URI uri; - try { - uri = new URIBuilder(serviceLocation + "/rest/issue/" + issueId).build(); - } catch (URISyntaxException e) { - throw new RuntimeException(e); - } - final HttpPost request = createPostRequest(uri, new BasicNameValuePair(Fields.summary.name(), summary), - new BasicNameValuePair(Fields.description.name(), description)); - final HttpResponse response = httpClient.execute(request); - final StatusLine statusLine = response.getStatusLine(); - final HttpEntity entity = response.getEntity(); - final String responseText = entity == null ? null : EntityUtils.toString(entity); - throwExceptionsIfNeeded(statusLine, responseText); + } + + public Enumeration getBundle(String customField) throws IOException, JAXBException + { + final URI uri; + try { + uri = new URIBuilder(serviceLocation + "/rest/admin/customfield/bundle/" + customField).build(); + } catch (URISyntaxException e) { + throw new RuntimeException(e); } - - private HttpPost createPostRequest(URI uri, NameValuePair... nameValuePair) throws UnsupportedEncodingException - { - return setEntity(new HttpPost(uri), nameValuePair); + final Object result = YoutrackUnmarshaller.unmarshall(execute(new HttpGet(uri))); + if (result instanceof Enumeration) { + return (Enumeration) result; + } else if (result instanceof JAXBElement) { + final JAXBElement jaxbElement = (JAXBElement) result; + if (Enumeration_QNAME.equals(jaxbElement.getName())) { + return (Enumeration) ((JAXBElement) result).getValue(); + } else { + throw new YoutrackAPIException("Unexpected type: " + jaxbElement.getValue()); + } + } else { + throw new YoutrackAPIException("Unexpected type: " + result); } - - private HttpPut createPutRequest(URI uri, NameValuePair... nameValuePair) throws UnsupportedEncodingException - { - return setEntity(new HttpPut(uri), nameValuePair); + } + + public List getIndividualAssignees(String project) throws IOException, JAXBException + { + final URI uri; + try { + uri = new URIBuilder(serviceLocation + "/rest/admin/project/" + project + "/assignee/individual").build(); + } catch (URISyntaxException e) { + throw new RuntimeException(e); } - - private String execute(HttpUriRequest request) throws IOException - { - final HttpResponse response = httpClient.execute(request); - final StatusLine statusLine = response.getStatusLine(); - final HttpEntity entity = response.getEntity(); - String responseText = entity == null ? null : EntityUtils.toString(entity); - if (statusLine.getStatusCode() >= 300) { - try { - final String error = ErrorUnmarshaller.unmarshal(responseText); - throw new YoutrackErrorException(error, statusLine.getStatusCode()); - } catch (JAXBException e) { - // TODO we should log on debug level the JAXBException - throw new HttpResponseException(statusLine.getStatusCode(), responseText); - } - } - if (entity == null) { - throw new ClientProtocolException("Response contains no content"); - } - return responseText; + final String responseString = execute(new HttpGet(uri)); + final Object result = YoutrackUnmarshaller.unmarshall(responseString); + if (result instanceof UserRefs) { + return ((UserRefs) result).getUsers(); + } else { + throw new YoutrackAPIException("Unexpected type: " + result); } - - private T setEntity(T request, NameValuePair[] nameValuePair) throws UnsupportedEncodingException - { - final ArrayList list = new ArrayList(); - Collections.addAll(list, nameValuePair); - request.setEntity(new UrlEncodedFormEntity(list)); - return request; + } + + public IssueWrapper getIssue(String issueId) throws IOException, JAXBException + { + final URI uri; + try { + uri = new URIBuilder(serviceLocation + "/rest/issue/" + issueId).build(); + } catch (URISyntaxException e) { + throw new RuntimeException(e); } - - private void throwExceptionsIfNeeded(StatusLine statusLine, String responseText) throws IOException - { - if (statusLine.getStatusCode() >= 300) { - throw new HttpResponseException(statusLine.getStatusCode(), responseText); - } + final String responseString; + try { + responseString = execute(new HttpGet(uri)); + } catch (YoutrackErrorException e) { + if (e.getStatusCode() == HttpStatus.SC_NOT_FOUND) { + throw new NoResultFoundException(e.getMessage(), e); + } else { + throw e; + } + } + final Object result = YoutrackUnmarshaller.unmarshall(responseString); + if (result instanceof pl.com.it_crowd.youtrack.api.rest.Issue) { + return new IssueWrapper((Issue) result); + } else if (result instanceof JAXBElement) { + final JAXBElement jaxbElement = (JAXBElement) result; + if (Issue_QNAME.equals(jaxbElement.getName())) { + return new IssueWrapper((Issue) jaxbElement.getValue()); + } else { + throw new YoutrackAPIException("Unexpected type: " + jaxbElement.getValue()); + } + } else { + throw new YoutrackAPIException("Unexpected type " + result); + } + } + + public void login(String username, String password) throws IOException, JAXBException + { + final HttpPost request = new HttpPost(serviceLocation + "/rest/user/login"); + request.setEntity(new UrlEncodedFormEntity(Arrays.asList(new BasicNameValuePair("login", username), new BasicNameValuePair("password", password)))); + execute(request); + } + + public List searchIssuesByProject(String project, Object filter) throws JAXBException, IOException + { + final String url = serviceLocation + "/rest/issue/byproject/" + project + "?filter=" + (filter == null ? "" : filter); + final Object result = YoutrackUnmarshaller.unmarshall(execute(new HttpGet(url))); + if (!(result instanceof Issues)) { + throw new YoutrackAPIException("Unmarshalling problem. Expected Issues, received: " + result.getClass() + " " + result); + } + List issues = ((Issues) result).getIssues(); + List wrappedIssues = new ArrayList(); + for (Issue issue : issues) { + wrappedIssues.add(new IssueWrapper(issue)); + } + return wrappedIssues; + } + + public void updateIssue(String issueId, String summary, String description) throws IOException + { + final URI uri; + try { + uri = new URIBuilder(serviceLocation + "/rest/issue/" + issueId).build(); + } catch (URISyntaxException e) { + throw new RuntimeException(e); + } + final HttpPost request = createPostRequest(uri, new BasicNameValuePair(Fields.summary.name(), summary), + new BasicNameValuePair(Fields.description.name(), description)); + final HttpResponse response = httpClient.execute(request); + final StatusLine statusLine = response.getStatusLine(); + final HttpEntity entity = response.getEntity(); + final String responseText = entity == null ? null : EntityUtils.toString(entity); + throwExceptionsIfNeeded(statusLine, responseText); + } + + private HttpPost createPostRequest(URI uri, NameValuePair... nameValuePair) throws UnsupportedEncodingException + { + return setEntity(new HttpPost(uri), nameValuePair); + } + + private HttpPut createPutRequest(URI uri, NameValuePair... nameValuePair) throws UnsupportedEncodingException + { + return setEntity(new HttpPut(uri), nameValuePair); + } + + private String execute(HttpUriRequest request) throws IOException + { + final HttpResponse response = httpClient.execute(request); + final StatusLine statusLine = response.getStatusLine(); + final HttpEntity entity = response.getEntity(); + String responseText = entity == null ? null : EntityUtils.toString(entity); + if (statusLine.getStatusCode() >= 300) { + try { + final String error = ErrorUnmarshaller.unmarshal(responseText); + throw new YoutrackErrorException(error, statusLine.getStatusCode()); + } catch (JAXBException e) { + // TODO we should log on debug level the JAXBException + throw new HttpResponseException(statusLine.getStatusCode(), responseText); + } + } + if (entity == null) { + throw new ClientProtocolException("Response contains no content"); + } + return responseText; + } + + private T setEntity(T request, NameValuePair[] nameValuePair) throws UnsupportedEncodingException + { + final ArrayList list = new ArrayList(); + Collections.addAll(list, nameValuePair); + request.setEntity(new UrlEncodedFormEntity(list)); + return request; + } + + private void throwExceptionsIfNeeded(StatusLine statusLine, String responseText) throws IOException + { + if (statusLine.getStatusCode() >= 300) { + throw new HttpResponseException(statusLine.getStatusCode(), responseText); } + } } \ No newline at end of file