package hudson.plugins.sctmexecutor.service; import hudson.plugins.sctmexecutor.Messages; import hudson.plugins.sctmexecutor.exceptions.SCTMException; import java.io.IOException; import java.io.InputStream; import java.net.MalformedURLException; import java.net.URL; import java.rmi.RemoteException; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Collection; import java.util.logging.Level; import java.util.logging.Logger; import javax.xml.rpc.ServiceException; import org.apache.commons.httpclient.HttpClient; import org.apache.commons.httpclient.HttpException; import org.apache.commons.httpclient.HttpMethod; import org.apache.commons.httpclient.methods.GetMethod; import com.borland.sctm.ws.administration.MainEntities; import com.borland.sctm.ws.administration.MainEntitiesServiceLocator; import com.borland.sctm.ws.execution.ExecutionWebService; import com.borland.sctm.ws.execution.ExecutionWebServiceServiceLocator; import com.borland.sctm.ws.execution.entities.ExecutionHandle; import com.borland.sctm.ws.execution.entities.ExecutionNode; import com.borland.sctm.ws.execution.entities.ExecutionResult; import com.borland.sctm.ws.execution.entities.PropertyValue; import com.borland.sctm.ws.logon.SystemService; import com.borland.sctm.ws.logon.SystemServiceServiceLocator; import com.borland.sctm.ws.performer.PerformerService; import com.borland.sctm.ws.performer.PerformerServiceServiceLocator; import com.borland.sctm.ws.performer.SPNamedEntity; import com.borland.sctm.ws.planning.PlanningService; import com.borland.sctm.ws.planning.PlanningServiceServiceLocator; public class SCTMService implements ISCTMService { private static final int MAX_LOGONRETRYCOUNT = 3; private static final Logger LOGGER = Logger.getLogger("hudson.plugins.sctmservice"); //$NON-NLS-1$ private SystemService systemService; private ExecutionWebService execService; private MainEntities adminService; private PlanningService planningService; private PerformerService performerService; private long sessionId; private volatile int logonRetryCount; private String user; private String pwd; private int projectId; private String serviceExchangeURL; public SCTMService(String serviceURL, String user, String pwd, int projectId) throws SCTMException { try { this.user = user; this.pwd = pwd; this.projectId = projectId; systemService = new SystemServiceServiceLocator().getsccsystem(new URL(serviceURL + "/sccsystem?wsdl")); //$NON-NLS-1$ execService = new ExecutionWebServiceServiceLocator().gettmexecution(new URL(serviceURL + "/tmexecution?wsdl")); //$NON-NLS-1$ adminService = new MainEntitiesServiceLocator().getsccentities(new URL(serviceURL+"/sccentities?wsdl")); //$NON-NLS-1$ planningService = new PlanningServiceServiceLocator().gettmplanning(new URL(serviceURL+"/tmplanning?wsdl")); //$NON-NLS-1$ performerService = new PerformerServiceServiceLocator().gettmperformer(new URL(serviceURL+"/tmperformer?wsdl")); //$NON-NLS-1$ serviceExchangeURL = String.format("%sExchange?hid=%s", serviceURL, "SilkPerformer"); //$NON-NLS-1$ //$NON-NLS-2$ logon(); } catch (MalformedURLException e) { LOGGER.log(Level.SEVERE, e.getMessage(), e); throw new SCTMException(Messages.getString("SCTMService.err.serviceUrlWrong")); //$NON-NLS-1$ } catch (ServiceException e) { LOGGER.log(Level.SEVERE, e.getMessage(), e); throw new SCTMException(MessageFormat.format(Messages.getString("SCTMService.err.urlOrServiceBroken"), serviceURL)); //$NON-NLS-1$ } catch (RemoteException e) { LOGGER.log(Level.SEVERE, e.getMessage(), e); throw new SCTMException(MessageFormat.format(Messages.getString("SCTMService.err.commonFatalError"), e.getMessage())); //$NON-NLS-1$ } } private void logon() throws RemoteException { this.sessionId = this.systemService.logonUser(this.user, this.pwd);; execService.setCurrentProject(sessionId, projectId); planningService.setCurrentProject(sessionId, String.valueOf(projectId)); } private boolean handleLostSessionException(RemoteException e) throws SCTMException { if (lostSessionExceptionThrown(e) && logonRetryCount < MAX_LOGONRETRYCOUNT) { //$NON-NLS-1$ logonRetryCount++; LOGGER.warning(Messages.getString("SCTMService.warn.SessionLostReconnect")); //$NON-NLS-1$ try { logon(); return true; } catch (RemoteException e1) { LOGGER.log(Level.SEVERE, e.getMessage(), e); if (e.getMessage().contains("Not logged in")) //$NON-NLS-1$ throw new SCTMException(Messages.getString("SCTMService.err.accessDenied")); //$NON-NLS-1$ else throw new SCTMException(MessageFormat.format(Messages.getString("SCTMService.err.commonFatalError"), e.getMessage())); //$NON-NLS-1$ } } return false; } private boolean lostSessionExceptionThrown(RemoteException e) { String message = e.getMessage(); return message.contains("Not logged in.") || message.contains("Connection timed out") || //$NON-NLS-1$ //$NON-NLS-2$ (message.contains("InvalidIdException: sid") && message.contains("is invalid or expired")) || // InvalidIdException is not exposed as class in the wsdl //$NON-NLS-1$ //$NON-NLS-2$ (message.contains("InvalidSidException: sid") && message.contains("is invalid or expired")); // InvalidSidException is not exposed as class in the wsdl //$NON-NLS-1$ //$NON-NLS-2$ } private Collection<ExecutionHandle> convertToList(ExecutionHandle[] handles) { Collection<ExecutionHandle> runs = new ArrayList<ExecutionHandle>(handles.length); for (ExecutionHandle handle : handles) { runs.add(handle); } return runs; } private Collection<String> toList(String[] versions) { Collection<String> c = new ArrayList<String>(versions.length); for (String version : versions) { c.add(version); } return c; } private String getExecutionNodePropertyValue(ExecutionNode node, String propertyName) { PropertyValue[] propertyValues = node.getPropertyValues(); for (PropertyValue propertyValue : propertyValues) { if (propertyName.equals(propertyValue.getName())) return propertyValue.getValue(); } return null; } private ExecutionNode getExecDefNode(int nodeId) throws RemoteException, SCTMException { ExecutionNode node = execService.getNode(sessionId, nodeId); if (node == null) throw new SCTMException(MessageFormat.format(Messages.getString("SCTMService.err.missExecDef"), nodeId)); //$NON-NLS-1$ return node; } private String getProductName(ExecutionNode node) throws RemoteException { String testContainerId = getExecutionNodePropertyValue(node, "PROP_TESTCONTAINER"); //$NON-NLS-1$ String productId = planningService.getProperty(sessionId, testContainerId, "_node_properties_ProductID_pk_fk").getValue(); //$NON-NLS-1$ String productName = adminService.getProductNameById(sessionId, Integer.valueOf(productId)); return productName; } /* (non-Javadoc) * @see hudson.plugins.sctmexecutor.service.ISCTMService#start(int) */ public Collection<ExecutionHandle> start(int executionId) throws SCTMException { try { ExecutionHandle[] handles = execService.startExecution(this.sessionId, executionId); logonRetryCount = 0; return convertToList(handles); } catch (RemoteException e) { if (handleLostSessionException(e)) return start(executionId); LOGGER.log(Level.WARNING, e.getMessage(), e); throw new SCTMException(MessageFormat.format(Messages.getString("SCTMService.err.commonFatalError"), e.getMessage())); //$NON-NLS-1$ } } /* (non-Javadoc) * @see hudson.plugins.sctmexecutor.service.ISCTMService#start(int, java.lang.String) */ public Collection<ExecutionHandle> start(int executionId, String buildNumber) throws SCTMException { try { ExecutionHandle[] handles = execService.startExecution(this.sessionId, executionId, buildNumber, 1, null); return convertToList(handles); } catch (RemoteException e) { if (handleLostSessionException(e)) { return start(executionId, buildNumber); } LOGGER.log(Level.WARNING, e.getMessage(), e); throw new SCTMException(MessageFormat.format(Messages.getString("SCTMService.err.commonFatalError"), e.getMessage())); //$NON-NLS-1$ } } /* (non-Javadoc) * @see hudson.plugins.sctmexecutor.service.ISCTMService#isFinished(com.borland.tm.webservices.tmexecution.ExecutionHandle) */ public boolean isFinished(ExecutionHandle handle) throws SCTMException { try { return execService.getStateOfExecution(sessionId, handle) < 0; } catch (RemoteException e) { if (handleLostSessionException(e)) return isFinished(handle); LOGGER.log(Level.SEVERE, e.getMessage(), e); throw new SCTMException(MessageFormat.format(Messages.getString("SCTMService.err.commonFatalError"), e.getMessage())); //$NON-NLS-1$ } } /* (non-Javadoc) * @see hudson.plugins.sctmexecutor.service.ISCTMService#getExecutionResult(com.borland.tm.webservices.tmexecution.ExecutionHandle) */ public ExecutionResult getExecutionResult(ExecutionHandle handle) throws SCTMException { try { return execService.getExecutionResult(this.sessionId, handle); } catch (RemoteException e) { if (handleLostSessionException(e)) return getExecutionResult(handle); LOGGER.log(Level.SEVERE, e.getMessage(), e); throw new SCTMException(MessageFormat.format(Messages.getString("SCTMService.err.commonFatalError"), e.getMessage())); //$NON-NLS-1$ } } @Override public boolean addBuildNumber(String productName, String version, int buildNumber) throws SCTMException { try { return adminService.addBuild(sessionId, productName, version, String.valueOf(buildNumber), Messages.getString("SCTMService.msg.sctm.buildnumberComment"), true); //$NON-NLS-1$ } catch (RemoteException e) { if (handleLostSessionException(e)) addBuildNumber(productName, version, buildNumber); LOGGER.log(Level.SEVERE, e.getMessage(), e); throw new SCTMException(MessageFormat.format(Messages.getString("SCTMService.err.commonFatalError"), e.getMessage())); //$NON-NLS-1$ } } @Override public boolean buildNumberExists(String productName, String version, int buildNumber) throws SCTMException { try { String[] builds = adminService.getBuilds(sessionId, productName, version); String value = String.valueOf(buildNumber); for (String build : builds) { if (value.equals(build)) return true; } return false; } catch (RemoteException e) { if (handleLostSessionException(e)) return buildNumberExists(productName, version, buildNumber); LOGGER.log(Level.SEVERE, e.getMessage(), e); throw new SCTMException(MessageFormat.format(Messages.getString("SCTMService.err.commonFatalError"), e.getMessage())); //$NON-NLS-1$ } } @Override public int getLatestSCTMBuildnumber(String productName, String version) throws SCTMException { try { String[] builds = adminService.getBuilds(sessionId, productName, version); int latestBuildnumber = -1; for (String bn : builds) { int buildnumber = 0; try { buildnumber = Integer.parseInt(bn); if (buildnumber > latestBuildnumber) latestBuildnumber = buildnumber; } catch (NumberFormatException e) { LOGGER.warning(MessageFormat.format(Messages.getString("SCTMService.err.buildnumberConvertion"), buildnumber)); //$NON-NLS-1$ } } return latestBuildnumber; } catch (RemoteException e) { if (handleLostSessionException(e)) return getLatestSCTMBuildnumber(productName, version); LOGGER.log(Level.SEVERE, e.getMessage(), e); throw new SCTMException(MessageFormat.format(Messages.getString("SCTMService.err.commonFatalError"), e.getMessage())); //$NON-NLS-1$ } } @Override public String getExecDefinitionName(int execDefId) throws SCTMException { try { ExecutionNode node = getExecDefNode(execDefId); return getExecutionNodePropertyValue(node, "PROP_NAME"); //$NON-NLS-1$ } catch (RemoteException e) { if (handleLostSessionException(e)) return getExecDefinitionName(execDefId); LOGGER.log(Level.SEVERE, e.getMessage(), e); throw new SCTMException(MessageFormat.format(Messages.getString("SCTMService.err.commonFatalError"), e.getMessage())); //$NON-NLS-1$ } } @Override public Collection<String> getAllVersions(int execDefId) throws SCTMException { String[] versions = new String[0]; try { ExecutionNode node = getExecDefNode(execDefId); versions = adminService.getVersions(execDefId, getProductName(node )); } catch (RemoteException e) { if (handleLostSessionException(e)) return getAllVersions(execDefId); LOGGER.log(Level.SEVERE, e.getMessage(), e); throw new SCTMException(MessageFormat.format(Messages.getString("SCTMService.err.commonFatalError"), e.getMessage())); //$NON-NLS-1$ } return toList(versions); } @Override public SPNamedEntity[] getResultFiles(int testDefRunId) throws SCTMException { try { return performerService.getExecutionFiles(sessionId, testDefRunId); } catch (RemoteException e) { if (handleLostSessionException(e)) return getResultFiles(testDefRunId); LOGGER.log(Level.SEVERE, e.getMessage(), e); throw new SCTMException(MessageFormat.format(Messages.getString("SCTMService.err.commonFatalError"), e.getMessage())); //$NON-NLS-1$ } } @Override public InputStream loadResultFile(int fileId) { try { URL url = new URL(String.format("%s&sid=%s&rfid=%s", this.serviceExchangeURL, this.sessionId, fileId)); //$NON-NLS-1$ HttpClient client = new HttpClient(); HttpMethod get = new GetMethod(url.toExternalForm()); client.executeMethod(get); return get.getResponseBodyAsStream(); } catch (MalformedURLException e) { LOGGER.log(Level.SEVERE, Messages.getString("SCTMService.err.malformedURL"), e); //$NON-NLS-1$ } catch (HttpException e) { LOGGER.log(Level.FINE, Messages.getString("SCTMService.err.loadingResults"), e); //$NON-NLS-1$ } catch (IOException e) { LOGGER.log(Level.SEVERE, Messages.getString("SCTMService.err.loadingResults"), e); //$NON-NLS-1$ } return null; } @Override public String getProductName(int nodeId) throws SCTMException { try { return getProductName(getExecDefNode(nodeId)); } catch (RemoteException e) { if (handleLostSessionException(e)) return getProductName(nodeId); LOGGER.log(Level.SEVERE, e.getMessage(), e); throw new SCTMException(MessageFormat.format(Messages.getString("SCTMService.err.commonFatalError"), e.getMessage())); //$NON-NLS-1$ } } }