/******************************************************************************* * Copyright (c) 2007 - 2007 IT Solutions, Inc. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Chris Hane - Initial implementation for Mantis *******************************************************************************/ package com.itsolut.mantis.core.soap; import java.math.BigInteger; import java.net.MalformedURLException; import java.net.URL; import java.rmi.RemoteException; import java.util.Arrays; import java.util.List; import java.util.Locale; import java.util.concurrent.Callable; import javax.xml.rpc.Call; import javax.xml.rpc.ServiceException; import javax.xml.rpc.Stub; import org.apache.axis.AxisFault; import org.apache.axis.configuration.FileProvider; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.mylyn.commons.net.AbstractWebLocation; import org.eclipse.mylyn.commons.net.AuthenticationCredentials; import org.eclipse.mylyn.commons.net.AuthenticationType; import org.eclipse.mylyn.commons.net.Policy; import org.eclipse.mylyn.internal.provisional.commons.soap.AbstractSoapClient; import org.xml.sax.SAXException; import biz.futureware.mantis.rpc.soap.client.*; import com.google.common.collect.Lists; import com.itsolut.mantis.core.SourceForgeConstants; import com.itsolut.mantis.core.exception.MantisException; import com.itsolut.mantis.core.exception.MantisLocalException; import com.itsolut.mantis.core.exception.MantisRemoteException; import com.itsolut.mantis.core.exception.TicketNotFoundException; import com.itsolut.mantis.core.model.MantisIssueHistory; import com.itsolut.mantis.core.model.MantisIssueHistoryEntry; /** * Represents a Mantis repository that is accessed through the MantisConnect SOAP Interface. * * @author Chris Hane */ @SuppressWarnings("restriction") public class MantisAxis1SoapClient extends AbstractSoapClient { /** * The default location of the SOAP API entry point, as of Mantis 1.1 */ public static final String SOAP_API_LOCATION = "/api/soap/mantisconnect.php"; private transient MantisConnectPortType soap; private AbstractWebLocation location; public MantisAxis1SoapClient(AbstractWebLocation webLocation) throws MantisException { synchronized ( this ) { this.location = webLocation; soap = this.getSOAP(); configureHttpAuthentication(); } } private void configureHttpAuthentication() { AuthenticationCredentials httpCredentials = location.getCredentials(AuthenticationType.HTTP); if (httpCredentials == null) return; Stub stub = (Stub) soap; stub._setProperty(Call.USERNAME_PROPERTY, httpCredentials.getUserName()); stub._setProperty(Call.PASSWORD_PROPERTY, httpCredentials.getPassword()); } private boolean doesNotHaveHttpAuth() { return location.getCredentials(AuthenticationType.HTTP) == null; } @Override protected AbstractWebLocation getLocation() { return location; } private String getUsername() { AuthenticationCredentials credentials = location.getCredentials(AuthenticationType.REPOSITORY); if ( credentials == null) return null; return credentials.getUserName(); } private String getPassword() { AuthenticationCredentials credentials = location.getCredentials(AuthenticationType.REPOSITORY); if ( credentials == null) return null; return location.getCredentials(AuthenticationType.REPOSITORY).getPassword(); } protected MantisConnectPortType getSOAP() throws MantisException { synchronized (this) { if (soap != null) { return soap; } try { FileProvider provider = new FileProvider(getClass().getResourceAsStream( "client-config.wsdd")); CustomMantisConnectLocator locator = new CustomMantisConnectLocator(provider); locator.setLocation(getLocation()); soap = locator.getMantisConnectPort(new URL(location.getUrl() + SOAP_API_LOCATION)); } catch (ServiceException e) { throw new MantisRemoteException(e); } catch (MalformedURLException e) { throw new MantisLocalException(e); } return soap; } } @Override protected boolean doLogin(IProgressMonitor monitor) { return true; } @Override protected boolean isAuthenticationException(Exception exception) { if (!(exception instanceof MantisException)) return false; MantisException mantisException = (MantisException) exception; return mantisException.getMessage() != null && mantisException.getMessage().toLowerCase(Locale.ENGLISH).indexOf("access denied") != -1; } @Override protected <T> T call(IProgressMonitor monitor, Callable<T> runnable) throws MantisException { try { return super.call(monitor, runnable); } catch (MantisException e) { throw e; } catch (RemoteException e) { throw wrap(e); } catch (RuntimeException e) { throw e; } catch (Error e) { throw e; } catch (Exception e) { throw new RuntimeException(e); } } @Override protected <T> T callOnce(IProgressMonitor monitor, Callable<T> runnable) throws MantisException { try { return super.callOnce(monitor, runnable); } catch (MantisException e) { throw e; } catch (RemoteException e) { throw wrap(e); } catch (RuntimeException e) { throw e; } catch (Error e) { throw e; } catch (Exception e) { throw new RuntimeException(e); } } private MantisRemoteException wrap(RemoteException e) { boolean unexpected = false; StringBuilder message = new StringBuilder(); if (isSourceforgeRepoWithoutHttpAuth()) message.append("For SF.net hosted apps, please make sure to use HTTP authentication only.").append('\n'); if (location.getUrl().startsWith(SourceForgeConstants.OLD_SF_NET_URL)) message.append("SF.net hosted apps have been moved to https://sourceforge.net/apps/mantisbt/").append('\n'); if (e instanceof AxisFault) { AxisFault axisFault = (AxisFault) e; if (axisFault.getCause() instanceof SAXException) message.append("The repository has returned an invalid XML response : " + axisFault.getCause().getMessage()); else if (e.getMessage() != null) message.append(e.getMessage()); unexpected = true; } else if (e.getMessage() != null) message.append(e.getMessage()); return new MantisRemoteException(message.toString(), e, unexpected); } private boolean isSourceforgeRepoWithoutHttpAuth() { return location.getUrl().startsWith(SourceForgeConstants.NEW_SF_NET_URL) && doesNotHaveHttpAuth(); } public IssueData getIssueData(final int issueId, IProgressMonitor monitor) throws MantisException { return call(monitor, new Callable<IssueData>() { public IssueData call() throws MantisException, RemoteException { try { return getSOAP().mc_issue_get(getUsername(), getPassword(), BigInteger.valueOf(issueId)); } catch (RemoteException e) { if ( e.getMessage().startsWith("Issue does not exist")) throw new TicketNotFoundException(issueId); throw e; } } }); } public byte[] getIssueAttachment(final int attachmentID, final IProgressMonitor monitor) throws MantisException { byte[] attachment = call(monitor, new Callable<byte[]>() { public byte[] call() throws Exception { return getSOAP() .mc_issue_attachment_get(getUsername(), getPassword(), BigInteger.valueOf(attachmentID)); } }); return attachment; } public void addIssueAttachment(final int ticketID, final String filename, final byte[] data, final IProgressMonitor monitor) throws MantisException { call(monitor, new Callable<Void>() { public Void call() throws Exception { getSOAP().mc_issue_attachment_add(getUsername(), getPassword(), BigInteger.valueOf(ticketID), filename, "bug", data); return null; } }); } public void deleteIssueAttachment(final int attachmentId, final IProgressMonitor monitor) throws MantisException { call(monitor, new Callable<Void>() { public Void call() throws Exception { getSOAP().mc_issue_attachment_delete(getUsername(), getPassword(), BigInteger.valueOf(attachmentId)); return null; } }); } public IssueHeaderData[] getIssueHeaders(final int projectId, final int filterId, final int limit, IProgressMonitor monitor) throws MantisException { return call(monitor, new Callable<IssueHeaderData[]>() { public IssueHeaderData[] call() throws Exception { return getSOAP().mc_filter_get_issue_headers(getUsername(), getPassword(), BigInteger.valueOf(projectId), // project BigInteger.valueOf(filterId), // filter BigInteger.ONE, // start page BigInteger.valueOf(limit)); // # per page } }); } public IssueHeaderData[] getIssueHeaders(final int projectId, final int limit, IProgressMonitor monitor) throws MantisException { return call(monitor, new Callable<IssueHeaderData[]>() { public IssueHeaderData[] call() throws Exception { return getSOAP().mc_project_get_issue_headers(getUsername(), getPassword(), BigInteger.valueOf(projectId), BigInteger.ONE, BigInteger.valueOf(limit)); } }); } public int addIssue(final IssueData issue, IProgressMonitor monitor) throws MantisException { return call(monitor, new Callable<BigInteger>() { public BigInteger call() throws Exception { BigInteger result = getSOAP().mc_issue_add(getUsername(), getPassword(), issue); return result; } }).intValue(); } public void addRelationship(final int ticketId, final RelationshipData relationshipData, IProgressMonitor monitor) throws MantisException { call(monitor, new Callable<Void>() { public Void call() throws Exception { getSOAP().mc_issue_relationship_add(getUsername(), getPassword(), BigInteger.valueOf(ticketId), relationshipData); return null; } }); } public void deleteRelationship(final int ticketId, final int relationshipId, IProgressMonitor monitor) throws MantisException { call(monitor, new Callable<Void>() { public Void call() throws Exception { getSOAP().mc_issue_relationship_delete(getUsername(), getPassword(), BigInteger.valueOf(ticketId), BigInteger.valueOf(relationshipId)); return null; } }); } public void addNote(final int issueId, final IssueNoteData ind, IProgressMonitor monitor) throws MantisException { call(monitor, new Callable<Void>() { public Void call() throws Exception { getSOAP().mc_issue_note_add(getUsername(), getPassword(), BigInteger.valueOf(issueId), ind); return null; } }); } public void updateIssue(final IssueData issue, IProgressMonitor monitor) throws MantisException { call(monitor, new Callable<Void>() { public Void call() throws Exception { getSOAP().mc_issue_update(getUsername(), getPassword(), issue.getId(), issue); return null; } }); } public void deleteIssue(final int issueId, IProgressMonitor monitor) throws MantisException { call(monitor, new Callable<Void>() { public Void call() throws Exception { getSOAP().mc_issue_delete(getUsername(), getPassword(), BigInteger.valueOf(issueId)); return null; } }); } public ProjectData[] getProjectData(IProgressMonitor monitor) throws MantisException { return call(monitor, new Callable<ProjectData[]>() { public ProjectData[] call() throws Exception { return getSOAP().mc_projects_get_user_accessible(getUsername(), getPassword()); } }); } public FilterData[] getProjectFilters(final int projectId, IProgressMonitor monitor) throws MantisException { return call(monitor, new Callable<FilterData[]>() { public FilterData[] call() throws Exception { return getSOAP().mc_filter_get(getUsername(), getPassword(), BigInteger.valueOf(projectId)); } }); } public CustomFieldDefinitionData[] getProjectCustomFields(final int projectId, IProgressMonitor monitor) throws MantisException { return call(monitor, new Callable<CustomFieldDefinitionData[]>() { public CustomFieldDefinitionData[] call() throws Exception { return getSOAP().mc_project_get_custom_fields(getUsername(), getPassword(), BigInteger.valueOf(projectId)); } }); } public String getVersion(IProgressMonitor monitor) throws MantisException { return call(monitor, new Callable<String>() { public String call() throws Exception { return getSOAP().mc_version(); } }); } public String getStringConfiguration(IProgressMonitor monitor, final String configurationParameter) throws MantisException { return call(monitor, new Callable<String>() { public String call() throws Exception { return getSOAP().mc_config_get_string(getUsername(), getPassword(), configurationParameter); } }); } public ObjectRef[] getPriorities(IProgressMonitor monitor) throws MantisException { return call(monitor, new Callable<ObjectRef[]>() { public ObjectRef[] call() throws Exception { return getSOAP().mc_enum_priorities(getUsername(), getPassword()); } }); } public ObjectRef[] getStatuses(IProgressMonitor monitor) throws MantisException { return call(monitor, new Callable<ObjectRef[]>() { public ObjectRef[] call() throws Exception { return getSOAP().mc_enum_status(getUsername(), getPassword()); } }); } public ObjectRef[] getSeverities(IProgressMonitor monitor) throws MantisException { return call(monitor, new Callable<ObjectRef[]>() { public ObjectRef[] call() throws Exception { return getSOAP().mc_enum_severities(getUsername(), getPassword()); } }); } public ObjectRef[] getResolutions(IProgressMonitor monitor) throws MantisException { return call(monitor, new Callable<ObjectRef[]>() { public ObjectRef[] call() throws Exception { return getSOAP().mc_enum_resolutions(getUsername(), getPassword()); } }); } public ObjectRef[] getReproducibilities(IProgressMonitor monitor) throws MantisException { return call(monitor, new Callable<ObjectRef[]>() { public ObjectRef[] call() throws Exception { return getSOAP().mc_enum_reproducibilities(getUsername(), getPassword()); } }); } public ObjectRef[] getProjections(IProgressMonitor monitor) throws MantisException { return call(monitor, new Callable<ObjectRef[]>() { public ObjectRef[] call() throws Exception { return getSOAP().mc_enum_projections(getUsername(), getPassword()); } }); } public ObjectRef[] getEtas(IProgressMonitor monitor) throws MantisException { return call(monitor, new Callable<ObjectRef[]>() { public ObjectRef[] call() throws Exception { return getSOAP().mc_enum_etas(getUsername(), getPassword()); } }); } public ObjectRef[] getViewStates(IProgressMonitor monitor) throws MantisException { return call(monitor, new Callable<ObjectRef[]>() { public ObjectRef[] call() throws Exception { return getSOAP().mc_enum_view_states(getUsername(), getPassword()); } }); } public String[] getProjectCategories(final int value, IProgressMonitor monitor) throws MantisException { return call(monitor, new Callable<String[]>() { public String[] call() throws Exception { return getSOAP().mc_project_get_categories(getUsername(), getPassword(), BigInteger.valueOf(value)); } }); } public AccountData[] getProjectUsers(final int projectId, final int reporterThreshold, IProgressMonitor monitor) throws MantisException { return call(monitor, new Callable<AccountData[]>() { public AccountData[] call() throws Exception { return getSOAP().mc_project_get_users(getUsername(), getPassword(), BigInteger.valueOf(projectId), BigInteger.valueOf(reporterThreshold)); } }); } public ProjectVersionData[] getProjectVersions(final int projectId, IProgressMonitor monitor) throws MantisException { return call(monitor, new Callable<ProjectVersionData[]>() { public ProjectVersionData[] call() throws Exception { return getSOAP().mc_project_get_versions(getUsername(), getPassword(), BigInteger.valueOf(projectId)); } }); } public TagDataSearchResult getTags(final int pageNumber, final int perPage, IProgressMonitor monitor) throws MantisException { return call(monitor, new Callable<TagDataSearchResult>() { public TagDataSearchResult call() throws Exception { return getSOAP().mc_tag_get_all(getUsername(), getPassword(), BigInteger.valueOf(pageNumber), BigInteger.valueOf(perPage)); }} ); } /** * Convenience method to retrieve all tags without needing to consider multiple pages * * @param pageSize the number of tags to retrieve per individual call * @param monitor the progress monitor, used only to check for cancellation * @return all the tags * @throws MantisException */ public List<TagData> getAllTags(final int pageSize, IProgressMonitor monitor) throws MantisException { int page = 1; List<TagData> allTags = Lists.newArrayList(); while ( true ) { TagDataSearchResult tagResult = getTags(page, pageSize, monitor); Policy.checkCanceled(monitor); if ( tagResult.getResults() == null || tagResult.getResults().length == 0 ) break; allTags.addAll(Arrays.asList(tagResult.getResults())); if ( allTags.size() >= tagResult.getTotal_results().intValue() ) break; page++; } return allTags; } public HistoryData[] getHistory(final int issueId, IProgressMonitor monitor) throws MantisException { return call(monitor, new Callable<HistoryData[]>() { public HistoryData[] call() throws Exception { return getSOAP().mc_issue_get_history(getUsername(), getPassword(), BigInteger.valueOf(issueId)); } }); } }