PageRenderTime 60ms CodeModel.GetById 34ms RepoModel.GetById 1ms app.codeStats 0ms

/dtkit/src/main/java/org/jenkinsci/plugins/dtkit/DTKitBuilder.java

https://github.com/jenkinsci/dtkit-plugin
Java | 445 lines | 345 code | 64 blank | 36 comment | 45 complexity | c7a1dbb913898550b9465340d777f0fa MD5 | raw file
  1. package org.jenkinsci.plugins.dtkit;
  2. import com.google.inject.AbstractModule;
  3. import com.google.inject.Guice;
  4. import com.google.inject.Singleton;
  5. import hudson.DescriptorExtensionList;
  6. import hudson.Extension;
  7. import hudson.FilePath;
  8. import hudson.Launcher;
  9. import hudson.Util;
  10. import hudson.model.AbstractBuild;
  11. import hudson.model.AbstractProject;
  12. import hudson.model.BuildListener;
  13. import hudson.model.Descriptor;
  14. import hudson.model.ParameterValue;
  15. import hudson.model.ParametersAction;
  16. import hudson.model.Result;
  17. import hudson.model.StringParameterValue;
  18. import hudson.tasks.BuildStepDescriptor;
  19. import hudson.tasks.BuildStepMonitor;
  20. import hudson.tasks.Builder;
  21. import java.io.File;
  22. import java.io.IOException;
  23. import java.util.ArrayList;
  24. import java.util.List;
  25. import java.util.ListIterator;
  26. import net.sf.json.JSONObject;
  27. import org.jenkinsci.lib.dtkit.descriptor.CoverageTypeDescriptor;
  28. import org.jenkinsci.lib.dtkit.descriptor.MeasureTypeDescriptor;
  29. import org.jenkinsci.lib.dtkit.descriptor.TestTypeDescriptor;
  30. import org.jenkinsci.lib.dtkit.descriptor.ViolationsTypeDescriptor;
  31. import org.jenkinsci.lib.dtkit.type.CoverageType;
  32. import org.jenkinsci.lib.dtkit.type.MeasureType;
  33. import org.jenkinsci.lib.dtkit.type.MetricsType;
  34. import org.jenkinsci.lib.dtkit.type.TestType;
  35. import org.jenkinsci.lib.dtkit.type.ViolationsType;
  36. import org.jenkinsci.plugins.dtkit.service.DTKitBuilderConversionService;
  37. import org.jenkinsci.plugins.dtkit.service.DTKitBuilderLog;
  38. import org.jenkinsci.plugins.dtkit.service.DTKitBuilderValidationService;
  39. import org.jenkinsci.plugins.dtkit.service.DTKitReportProcessingService;
  40. import org.jenkinsci.plugins.dtkit.transformer.DTKitBuilderToolInfo;
  41. import org.jenkinsci.plugins.dtkit.transformer.DTKitBuilderTransformer;
  42. import org.kohsuke.stapler.StaplerRequest;
  43. /**
  44. * @author Gregory Boissinot
  45. */
  46. public class DTKitBuilder extends Builder {
  47. private final String rootFolder;
  48. private transient final String generatedFolder;
  49. private transient final String generatedTests;
  50. private transient final String generatedCoverage;
  51. private transient final String generatedMeasures;
  52. private transient final String generatedViolations;
  53. private TestType[] tests;
  54. private CoverageType[] coverages;
  55. private ViolationsType[] violations;
  56. private MeasureType[] measures;
  57. private static final String DEFAULT_ROOT_PATH = "generatedDTKITFiles";
  58. private static final String DEFAULT_TEST_PATH = "/TESTS";
  59. private static final String DEFAULT_COVERAGE_PATH = "/COVERAGE";
  60. private static final String DEFAULT_MEASURES_PATH = "/MEASURES";
  61. private static final String DEFAULT_VIOLATIONS_PATH = "/VIOLATIONS";
  62. /**
  63. * Represents checkbox value in plugin configuration page
  64. */
  65. private boolean selectedForSuppress;
  66. public DTKitBuilder(TestType[] tests,
  67. CoverageType[] coverages,
  68. ViolationsType[] violations,
  69. MeasureType[] measures,
  70. String rootFolderPath,
  71. boolean selectedForSuppress
  72. ){
  73. this.tests = tests;
  74. this.coverages = coverages;
  75. this.violations = violations;
  76. this.measures = measures;
  77. this.selectedForSuppress = selectedForSuppress;
  78. if (rootFolderPath == null || rootFolderPath.trim().isEmpty()){
  79. this.rootFolder = DEFAULT_ROOT_PATH;
  80. this.generatedFolder = DEFAULT_ROOT_PATH;
  81. } else {
  82. this.rootFolder = rootFolderPath;
  83. if (new File(rootFolderPath).isAbsolute()){
  84. this.generatedFolder = DEFAULT_ROOT_PATH;
  85. } else {
  86. this.generatedFolder = rootFolderPath;
  87. }
  88. }
  89. this.generatedTests = generatedFolder + DEFAULT_TEST_PATH;
  90. this.generatedCoverage = generatedFolder + DEFAULT_COVERAGE_PATH;
  91. this.generatedMeasures = generatedFolder + DEFAULT_MEASURES_PATH;
  92. this.generatedViolations = generatedFolder + DEFAULT_VIOLATIONS_PATH;
  93. }
  94. public String getRootFolder() {
  95. return rootFolder;
  96. }
  97. public TestType[] getTests() {
  98. return tests;
  99. }
  100. public CoverageType[] getCoverages() {
  101. return coverages;
  102. }
  103. public ViolationsType[] getViolations() {
  104. return violations;
  105. }
  106. public MeasureType[] getMeasures() {
  107. return measures;
  108. }
  109. /**
  110. * Used with DTKit plugin configuration.
  111. * When selectedForSuppress, this option allows to suppress old build files prior to execute a new build
  112. */
  113. public boolean isSelectedForSuppress() {
  114. return selectedForSuppress;
  115. }
  116. public boolean getSelectedForSuppress() {
  117. return selectedForSuppress;
  118. }
  119. public void setTests(TestType[] tests) {
  120. this.tests = tests;
  121. }
  122. public void setCoverages(CoverageType[] coverages) {
  123. this.coverages = coverages;
  124. }
  125. public void setViolations(ViolationsType[] violations) {
  126. this.violations = violations;
  127. }
  128. public void setMeasures(MeasureType[] measures) {
  129. this.measures = measures;
  130. }
  131. public void setSelectedForSuppress(boolean selectedForSuppress) {
  132. this.selectedForSuppress = selectedForSuppress;
  133. }
  134. @Override
  135. public BuildStepMonitor getRequiredMonitorService() {
  136. return BuildStepMonitor.NONE;
  137. }
  138. @Override
  139. public boolean perform(final AbstractBuild<?, ?> build, Launcher launcher, final BuildListener listener)
  140. throws InterruptedException, IOException {
  141. final DTKitBuilderLog log = Guice.createInjector(new AbstractModule() {
  142. @Override
  143. protected void configure() {
  144. bind(BuildListener.class).toInstance(listener);
  145. }
  146. }).getInstance(DTKitBuilderLog.class);
  147. try {
  148. //Because of old jobs configuration, we have to check that those values are not null
  149. String rootFolder = this.rootFolder == null?(DEFAULT_ROOT_PATH):this.rootFolder;
  150. String generatedTests = this.generatedTests == null?(rootFolder+DEFAULT_TEST_PATH):this.generatedTests;
  151. String generatedCoverage = this.generatedCoverage == null?(rootFolder+DEFAULT_COVERAGE_PATH):this.generatedCoverage;
  152. String generatedMeasures = this.generatedMeasures == null?(rootFolder+DEFAULT_MEASURES_PATH):this.generatedMeasures;
  153. String generatedViolations = this.generatedViolations == null?(rootFolder+DEFAULT_VIOLATIONS_PATH):this.generatedViolations;
  154. final StringBuffer sb = new StringBuffer();
  155. // cause of eventual presence of last build files, need to check for files and to delete them if files exist
  156. // check for suppressing old build files
  157. if (this.isSelectedForSuppress()) {
  158. log.info("Checking for old build files...");
  159. this.removeOldBuildFiles(new FilePath(build.getWorkspace(), rootFolder),log);
  160. }
  161. log.info("Starting converting.");
  162. DTKitReportProcessingService processingService = Guice.createInjector(new AbstractModule() {
  163. @Override
  164. protected void configure() {
  165. bind(BuildListener.class).toInstance(listener);
  166. }
  167. }).getInstance(DTKitReportProcessingService.class);
  168. boolean isInvoked = false;
  169. if (new File(rootFolder).isAbsolute()){
  170. log.error("Tusar root folder musn't be absolute, result are generated in default folder: " +
  171. "[workspace]/generatedDTKITFiles.");
  172. }
  173. // Apply conversion for all tests tools
  174. if (tests.length != 0) {
  175. FilePath outputFileParent = new FilePath(build.getWorkspace(), generatedTests);
  176. outputFileParent.mkdirs();
  177. for (TestType testsType : tests) {
  178. log.info("Processing " + testsType.getDescriptor().getDisplayName());
  179. if (!processingService.isEmptyPattern(testsType.getPattern())) {
  180. boolean result = processInputMetricType(build, listener, testsType, outputFileParent);
  181. if (result) {
  182. isInvoked = true;
  183. }
  184. }
  185. }
  186. sb.append(";").append(generatedTests);
  187. }
  188. if (coverages.length != 0) {
  189. FilePath outputFileParent = new FilePath(build.getWorkspace(), generatedCoverage);
  190. outputFileParent.mkdirs();
  191. for (CoverageType coverageType : coverages) {
  192. log.info("Processing " + coverageType.getDescriptor().getDisplayName());
  193. if (!processingService.isEmptyPattern(coverageType.getPattern())) {
  194. boolean result = processInputMetricType(build, listener, coverageType, outputFileParent);
  195. if (result) {
  196. isInvoked = true;
  197. }
  198. }
  199. }
  200. sb.append(";").append(generatedCoverage);
  201. }
  202. if (violations.length != 0) {
  203. FilePath outputFileParent = new FilePath(build.getWorkspace(), generatedViolations);
  204. outputFileParent.mkdirs();
  205. for (ViolationsType violationsType : violations) {
  206. log.info("Processing " + violationsType.getDescriptor().getDisplayName());
  207. if (!processingService.isEmptyPattern(violationsType.getPattern())) {
  208. boolean result = processInputMetricType(build, listener, violationsType, outputFileParent);
  209. if (result) {
  210. isInvoked = true;
  211. }
  212. }
  213. }
  214. sb.append(";").append(generatedViolations);
  215. }
  216. if (measures.length != 0) {
  217. FilePath outputFileParent = new FilePath(build.getWorkspace(), generatedMeasures);
  218. outputFileParent.mkdirs();
  219. for (MeasureType measureType : measures) {
  220. log.info("Processing " + measureType.getDescriptor().getDisplayName());
  221. if (!processingService.isEmptyPattern(measureType.getPattern())) {
  222. boolean result = processInputMetricType(build, listener, measureType, outputFileParent);
  223. if (result) {
  224. isInvoked = true;
  225. }
  226. }
  227. }
  228. sb.append(";").append(generatedMeasures);
  229. }
  230. // Remove the first character
  231. sb.delete(0, 1);
  232. constructTUSARReportPathForSonar(build, sb.toString());
  233. if (!isInvoked) {
  234. log.error("Any files are correct. Fail build.");
  235. build.setResult(Result.FAILURE);
  236. return false;
  237. }
  238. return true;
  239. } catch (Throwable e) {
  240. e.printStackTrace();
  241. log.error("An exception occured. Message :"+e.getMessage()+". Please check the logs for full stack Trace. Fail build.");
  242. build.setResult(Result.FAILURE);
  243. return false;
  244. }
  245. }
  246. /**
  247. * Since it is possible to add several DTKit build steps, it is also possible to have several root folder.
  248. * This method will correctly update the sonar.tusar.reportsPaths property when a new dtkit build step is met.
  249. * @param build
  250. */
  251. private void constructTUSARReportPathForSonar(final AbstractBuild<?, ?> build, String reportPaths){
  252. boolean isTusarPropertyAlreadyAdded = false;
  253. ParametersAction a = build.getAction(ParametersAction.class);
  254. StringParameterValue parameter = (StringParameterValue) a.getParameter("sonar.tusar.reportsPaths");
  255. // If there is more than one dtkit build step, we have to update the
  256. // sonar.tusar.reportsPaths property
  257. if (null != parameter) {
  258. build.removeAction(a);
  259. List<ParameterValue> parameterValues = new ArrayList<ParameterValue>();
  260. parameterValues.add(new StringParameterValue("sonar.language", "tusar"));
  261. parameterValues.add(new StringParameterValue("sonar.tusar.reportsPaths", parameter.getValue() + ";" + reportPaths));
  262. build.addAction(new ParametersAction(parameterValues));
  263. isTusarPropertyAlreadyAdded = true;
  264. }
  265. //The first dtkit met build step is treated here
  266. if(false == isTusarPropertyAlreadyAdded) {
  267. List<ParameterValue> parameterValues = new ArrayList<ParameterValue>();
  268. parameterValues.add(new StringParameterValue("sonar.language", "tusar"));
  269. parameterValues.add(new StringParameterValue("sonar.tusar.reportsPaths", reportPaths));
  270. build.addAction(new ParametersAction(parameterValues));
  271. }
  272. }
  273. private boolean processInputMetricType(final AbstractBuild<?, ?> build, final BuildListener listener, MetricsType metricsType, FilePath outputFileParent) throws IOException, InterruptedException {
  274. //Retrieves the pattern
  275. String newExpandedPattern = metricsType.getPattern();
  276. newExpandedPattern = newExpandedPattern.replaceAll("[\t\r\n]+", " ");
  277. newExpandedPattern = Util.replaceMacro(newExpandedPattern, build.getEnvironment(listener));
  278. //Build a new build info
  279. final DTKitBuilderToolInfo toolInfo = new DTKitBuilderToolInfo(metricsType, new File(outputFileParent.toURI()), newExpandedPattern, build.getTimeInMillis());
  280. // Archiving tool reports into JUnit files
  281. DTKitBuilderTransformer dtkitBuilderTransformer = Guice.createInjector(new AbstractModule() {
  282. @Override
  283. protected void configure() {
  284. bind(BuildListener.class).toInstance(listener);
  285. bind(DTKitBuilderToolInfo.class).toInstance(toolInfo);
  286. bind(DTKitBuilderValidationService.class).in(Singleton.class);
  287. bind(DTKitBuilderConversionService.class).in(Singleton.class);
  288. bind(DTKitBuilderLog.class).in(Singleton.class);
  289. bind(DTKitReportProcessingService.class).in(Singleton.class);
  290. }
  291. }).getInstance(DTKitBuilderTransformer.class);
  292. return build.getWorkspace().act(dtkitBuilderTransformer);
  293. }
  294. /**
  295. * This method performs an iterative walk into output directory tree to delete all old build files if present
  296. * @param rootFile the root folder that contains all output sub-folders and files
  297. * @param log
  298. * @throws IOException
  299. * @throws InterruptedException
  300. */
  301. private void removeOldBuildFiles(FilePath rootFile, DTKitBuilderLog log) throws IOException, InterruptedException {
  302. if (rootFile ==null){
  303. log.warning("The given root folder is null, the clean operation is cancelled");
  304. return;
  305. }
  306. List<FilePath> dirList = rootFile.list(); //return null if the rootFile does not exist
  307. if (dirList ==null){
  308. log.warning("The given root folder does not exist, the clean operation is cancelled");
  309. return;
  310. }
  311. ListIterator<FilePath> dirListIterator = dirList.listIterator();
  312. List<FilePath> fileList;
  313. ListIterator<FilePath> fileListIterator;
  314. FilePath tempDir, tempFile;
  315. // check for output folders
  316. if (!dirList.isEmpty()) {
  317. while (dirListIterator.hasNext()) {
  318. tempDir = dirListIterator.next();
  319. fileList = tempDir.list();
  320. // check for output files into folder
  321. if ((!fileList.isEmpty())) {
  322. fileListIterator = fileList.listIterator();
  323. while (fileListIterator.hasNext()) {
  324. tempFile = fileListIterator.next();
  325. if (tempFile.exists()) {
  326. // and delete them if present
  327. tempFile.delete();
  328. }
  329. }
  330. }
  331. }
  332. }
  333. }
  334. @Extension
  335. public static final class TusarNotifierDescriptor extends BuildStepDescriptor<Builder> {
  336. public TusarNotifierDescriptor() {
  337. super(DTKitBuilder.class);
  338. load();
  339. }
  340. @SuppressWarnings("rawtypes")
  341. @Override
  342. public boolean isApplicable(Class<? extends AbstractProject> jobType) {
  343. return true;
  344. }
  345. @Override
  346. public String getHelpFile() {
  347. return "/plugin/dtkit/help.html";
  348. }
  349. @Override
  350. public String getDisplayName() {
  351. return "DTKit Conversion";
  352. }
  353. public DescriptorExtensionList<TestType, TestTypeDescriptor<?>> getListTestDescriptors() {
  354. return TestTypeDescriptor.all();
  355. }
  356. public DescriptorExtensionList<ViolationsType, ViolationsTypeDescriptor<?>> getListViolationDescriptors() {
  357. return ViolationsTypeDescriptor.all();
  358. }
  359. public DescriptorExtensionList<MeasureType, MeasureTypeDescriptor<?>> getListMeasureDescriptors() {
  360. return MeasureTypeDescriptor.all();
  361. }
  362. public DescriptorExtensionList<CoverageType, CoverageTypeDescriptor<?>> getListCoverageDescriptors() {
  363. return CoverageTypeDescriptor.all();
  364. }
  365. @Override
  366. public Builder newInstance(StaplerRequest req, JSONObject formData)
  367. throws FormException {
  368. List<TestType> tests = Descriptor.newInstancesFromHeteroList(req, formData, "tests", getListTestDescriptors());
  369. List<CoverageType> coverages = Descriptor.newInstancesFromHeteroList(req, formData, "coverages", getListCoverageDescriptors());
  370. List<ViolationsType> violations = Descriptor.newInstancesFromHeteroList(req, formData, "violations", getListViolationDescriptors());
  371. List<MeasureType> measures = Descriptor.newInstancesFromHeteroList(req, formData, "measures", getListMeasureDescriptors());
  372. return new DTKitBuilder(tests.toArray(new TestType[tests.size()]),
  373. coverages.toArray(new CoverageType[coverages.size()]),
  374. violations.toArray(new ViolationsType[violations.size()]),
  375. measures.toArray(new MeasureType[measures.size()]),
  376. formData.getString("rootFolder"),
  377. formData.getBoolean("selectedForSuppress")
  378. );
  379. }
  380. }
  381. }