/*
* Coverity Sonar Plugin
* Copyright (c) 2017 Synopsys, Inc
* support@coverity.com
*
* 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.
*/
package org.sonar.plugins.coverity.ws;
import com.coverity.ws.v9.*;
import javax.xml.namespace.QName;
import javax.xml.ws.BindingProvider;
import javax.xml.ws.handler.Handler;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Represents one Coverity Integrity Manager server. Abstracts functions like getting streams and defects.
*/
public class CIMClient {
public static final String COVERITY_WS_VERSION = "v9";
public static final String COVERITY_NAMESPACE = "http://ws.coverity.com/" + COVERITY_WS_VERSION;
public static final String CONFIGURATION_SERVICE_WSDL = "/ws/" + COVERITY_WS_VERSION + "/configurationservice?wsdl";
public static final String DEFECT_SERVICE_WSDL = "/ws/" + COVERITY_WS_VERSION + "/defectservice?wsdl";
private static final int GET_STREAM_DEFECTS_MAX_CIDS = 100;
/**
* The host name for the CIM server
*/
private final String host;
/**
* The port for the CIM server (this is the HTTP port and not the data port)
*/
private final int port;
/**
* Username for connecting to the CIM server
*/
private final String user;
/**
* Password for connecting to the CIM server
*/
private final String password;
/**
* Use SSL
*/
private final boolean useSSL;
/**
* cached webservice port for Defect service
*/
private transient DefectServiceService defectServiceService;
/**
* cached webservice port for Configuration service
*/
private transient ConfigurationServiceService configurationServiceService;
private transient Map<String, Long> projectKeys;
public CIMClient(String host, int port, String user, String password, boolean ssl) {
this.host = host;
this.port = port;
this.user = user;
this.password = password;
this.useSSL = ssl;
}
public String getHost() {
return host;
}
public int getPort() {
return port;
}
public String getUser() {
return user;
}
public String getPassword() {
return password;
}
public boolean isUseSSL() {
return useSSL;
}
/**
* The root URL for the CIM instance
*
* @return a url
* @throws java.net.MalformedURLException should not happen if host is valid
*/
public URL getURL() throws MalformedURLException {
return new URL(useSSL ? "https" : "http", host, port, "/");
}
/**
* Returns a Defect service client
*/
public DefectService getDefectService() throws IOException {
synchronized(this) {
if (defectServiceService == null) {
defectServiceService = new DefectServiceService(
new URL(getURL(), DEFECT_SERVICE_WSDL),
new QName(COVERITY_NAMESPACE, "DefectServiceService"));
}
ClassLoader cl = Thread.currentThread().getContextClassLoader();
try {
DefectService defectService = defectServiceService.getDefectServicePort();
attachAuthenticationHandler((BindingProvider) defectService);
return defectService;
} finally {
Thread.currentThread().setContextClassLoader(cl);
}
}
}
/**
* Attach an authentication handler to the web service, that uses the configured user and password
*/
private void attachAuthenticationHandler(BindingProvider service) {
service.getBinding().setHandlerChain(Arrays.<Handler>asList(new ClientAuthenticationHandlerWSS(user, password)));
}
/**
* Returns a Configuration service client
*/
public ConfigurationService getConfigurationService() throws IOException {
synchronized(this) {
if (configurationServiceService == null) {
// Create a Web Services port to the server
configurationServiceService = new ConfigurationServiceService(
new URL(getURL(), CONFIGURATION_SERVICE_WSDL),
new QName(COVERITY_NAMESPACE, "ConfigurationServiceService"));
}
ClassLoader cl = Thread.currentThread().getContextClassLoader();
try {
ConfigurationService configurationService = configurationServiceService.getConfigurationServicePort();
attachAuthenticationHandler((BindingProvider)configurationService);
return configurationService;
} finally {
Thread.currentThread().setContextClassLoader(cl);
}
}
}
/**
* Returns all merged defects on a given project.
*/
public List<MergedDefectDataObj> getDefects(String project) throws IOException, CovRemoteServiceException_Exception {
ProjectScopeDefectFilterSpecDataObj filterSpec = new ProjectScopeDefectFilterSpecDataObj();
ProjectIdDataObj projectId = new ProjectIdDataObj();
projectId.setName(project);
PageSpecDataObj pageSpec = new PageSpecDataObj();
pageSpec.setPageSize(2500);
List<MergedDefectDataObj> result = new ArrayList<MergedDefectDataObj>();
int defectCount = 0;
MergedDefectsPageDataObj defects = null;
do {
pageSpec.setStartIndex(defectCount);
defects = getDefectService().getMergedDefectsForProjectScope(projectId, filterSpec, pageSpec);
result.addAll(defects.getMergedDefects());
defectCount += defects.getMergedDefects().size();
} while(defectCount < defects.getTotalNumberOfRecords());
return result;
}
/**
* Returns a ProjectDataObj for a given project id.
*/
public ProjectDataObj getProject(String projectId) throws IOException, CovRemoteServiceException_Exception {
ProjectFilterSpecDataObj filterSpec = new ProjectFilterSpecDataObj();
filterSpec.setNamePattern(projectId);
List<ProjectDataObj> projects = getConfigurationService().getProjects(filterSpec);
if(projects.size() == 0) {
return null;
} else {
return projects.get(0);
}
}
/**
* Returns all projects on the current cim instance.
*/
public List<ProjectDataObj> getProjects() throws IOException, CovRemoteServiceException_Exception {
return getConfigurationService().getProjects(new ProjectFilterSpecDataObj());
}
/**
* Returns a map of <CID, StreamDefectDataObj>. It essentially calls getDefectService().getStreamDefects() on a
* specific list of MergedDefectDataObj. Then it takes the resulting List<StreamDefectDataObj> and creates a map
* with the CID of each element on that list as the key, and the actual object as value.
*/
public Map<Long, StreamDefectDataObj> getStreamDefectsForMergedDefects(List<MergedDefectDataObj> defects) throws IOException, CovRemoteServiceException_Exception {
Map<Long, MergedDefectDataObj> cids = new HashMap<Long, MergedDefectDataObj>();
Map<Long, StreamDefectDataObj> sddos = new HashMap<Long, StreamDefectDataObj>();
Map<Long, MergedDefectIdDataObj> mdidos = new HashMap<Long, MergedDefectIdDataObj>();
for(MergedDefectDataObj mddo : defects) {
cids.put(mddo.getCid(), mddo);
MergedDefectIdDataObj mdido = new MergedDefectIdDataObj();
mdido.setCid(mddo.getCid());
mdido.setMergeKey(mddo.getMergeKey());
mdidos.put(mddo.getCid(), mdido);
}
StreamDefectFilterSpecDataObj filter = new StreamDefectFilterSpecDataObj();
filter.setIncludeDefectInstances(true);
List<Long> cidList = new ArrayList<Long>(cids.keySet());
for(int i = 0; i < cidList.size(); i += GET_STREAM_DEFECTS_MAX_CIDS) {
List<Long> slice = cidList.subList(i, i + Math.min(GET_STREAM_DEFECTS_MAX_CIDS, cidList.size() - i));
List<MergedDefectIdDataObj> sliceMergedDefectIdDataObj = new ArrayList<MergedDefectIdDataObj>();
for(Long cid : slice){
sliceMergedDefectIdDataObj.add(mdidos.get(cid));
}
List<StreamDefectDataObj> temp = getDefectService().getStreamDefects(sliceMergedDefectIdDataObj, filter);
for(StreamDefectDataObj sddo : temp) {
sddos.put(sddo.getCid(), sddo);
}
}
return sddos;
}
}