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