| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.jackrabbit.filevault.maven.packaging.impl; |
| |
| import java.io.Closeable; |
| import java.io.File; |
| import java.io.IOException; |
| import java.nio.charset.StandardCharsets; |
| import java.nio.file.Files; |
| import java.nio.file.Path; |
| import java.text.MessageFormat; |
| import java.util.Collection; |
| import java.util.Map; |
| |
| import org.apache.commons.csv.CSVFormat; |
| import org.apache.commons.csv.CSVPrinter; |
| import org.apache.commons.lang3.StringUtils; |
| import org.apache.jackrabbit.vault.validation.ValidationExecutor; |
| import org.apache.jackrabbit.vault.validation.ValidationViolation; |
| import org.apache.jackrabbit.vault.validation.spi.ValidationContext; |
| import org.apache.jackrabbit.vault.validation.spi.ValidationMessageSeverity; |
| import org.apache.jackrabbit.vault.validation.spi.Validator; |
| import org.apache.maven.plugin.MojoFailureException; |
| import org.apache.maven.plugin.logging.Log; |
| import org.apache.maven.shared.utils.logging.MessageBuilder; |
| import org.apache.maven.shared.utils.logging.MessageUtils; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| import org.sonatype.plexus.build.incremental.BuildContext; |
| import org.sonatype.plexus.build.incremental.DefaultBuildContext; |
| |
| public class ValidationMessagePrinter implements Closeable { |
| |
| /** |
| * Set to {@code true} if at least one {@link ValidationViolation} has been given out |
| */ |
| private int noOfEmittedValidationMessagesWithLevelWarn = 0; |
| |
| /** |
| * Set to {@code true} if at least one validation violation with severity {@link ValidationMessageSeverity#ERROR} has been given out |
| */ |
| private int noOfEmittedValidationMessagesWithLevelError = 0; |
| |
| private final @Nullable Path csvReportFile; |
| private final @Nullable CSVPrinter csvPrinter; |
| |
| private final Log log; |
| |
| public ValidationMessagePrinter(@NotNull Log log, @Nullable Path csvReportFile) throws IOException { |
| this.log = log; |
| this.csvReportFile = csvReportFile; |
| if (csvReportFile != null) { |
| csvPrinter = new CSVPrinter(Files.newBufferedWriter(csvReportFile, StandardCharsets.UTF_8), CSVFormat.EXCEL); |
| csvPrinter.printRecord("Severity", "Validator ID", "Message", "File", "Line:Column", "Node Path"); |
| } else { |
| csvPrinter = null; |
| } |
| } |
| |
| /** |
| * |
| * @param violations |
| * @param buildContext |
| * @param baseDirectory the directory to which all absolute paths should be made relative (e.g. the Maven basedir or the content package path) |
| * @throws IOException |
| */ |
| public void printMessages(Collection<ValidationViolation> violations, BuildContext buildContext, Path baseDirectory) throws IOException { |
| for (ValidationViolation violation : violations) { |
| final int buildContextSeverity; |
| switch (violation.getSeverity()) { |
| case ERROR: |
| log.error(getDetailMessage(violation, baseDirectory)); |
| if (violation.getThrowable() != null) { |
| log.debug(violation.getThrowable()); |
| } |
| buildContextSeverity = BuildContext.SEVERITY_ERROR; |
| noOfEmittedValidationMessagesWithLevelError++; |
| break; |
| case WARN: |
| log.warn(getDetailMessage(violation, baseDirectory)); |
| if (violation.getThrowable() != null) { |
| log.debug(violation.getThrowable()); |
| } |
| noOfEmittedValidationMessagesWithLevelWarn++; |
| buildContextSeverity = BuildContext.SEVERITY_WARNING; |
| break; |
| case INFO: |
| log.info(getDetailMessage(violation, baseDirectory)); |
| buildContextSeverity = -1; |
| break; |
| default: |
| log.debug(getDetailMessage(violation, baseDirectory)); |
| buildContextSeverity = -1; |
| break; |
| } |
| |
| if (buildContextSeverity > 0) { |
| // only emit via build context inside eclipse, otherwise log from above is better! |
| if (!(buildContext instanceof DefaultBuildContext)) { |
| Path file; |
| if (violation.getAbsoluteFilePath() != null) { |
| file = violation.getAbsoluteFilePath(); |
| } else { |
| // take the base path |
| file = baseDirectory; |
| } |
| buildContext.addMessage(file.toFile(), violation.getLine(), violation.getColumn(), getMessage(violation), buildContextSeverity, violation.getThrowable()); |
| } |
| if (!buildContext.isIncremental() && csvPrinter != null) { |
| printToCsvFile(violation); |
| } |
| } |
| } |
| } |
| |
| private static String getMessage(ValidationViolation violation) { |
| StringBuilder message = new StringBuilder(); |
| if (violation.getValidatorId() != null) { |
| message.append(violation.getValidatorId()).append(": "); |
| } |
| message.append(violation.getMessage()); |
| return message.toString(); |
| } |
| |
| public void printUsedValidators(Log log, ValidationExecutor executor, ValidationContext context, boolean printUnusedValidators) { |
| String packageType = context.getProperties().getPackageType() != null ? context.getProperties().getPackageType().toString() : "unknown"; |
| log.info("Using " + executor.getAllValidatorsById().entrySet().size() + " validators for package of type " + packageType + ": " + ValidationMessagePrinter.getValidatorNames(executor, ", ")); |
| if (printUnusedValidators) { |
| Map<String, Validator> unusedValidatorsById = executor.getUnusedValidatorsById(); |
| if (!unusedValidatorsById.isEmpty()) { |
| log.warn("There are unused validators among those which are not executed: " + StringUtils.join(unusedValidatorsById.keySet(), ".")); |
| } |
| } |
| } |
| |
| private static String getDetailMessage(ValidationViolation violation, Path baseDirectory) { |
| MessageBuilder builder = MessageUtils.buffer(); |
| builder.strong("ValidationViolation: "); |
| builder.a(violation.getMessage()); |
| if (violation.getFilePath() != null) { |
| builder.a(" @ ").strong(baseDirectory.relativize(violation.getAbsoluteFilePath())); |
| } |
| if (violation.getLine() > 0) { |
| builder.strong(", line ").strong(violation.getLine()); |
| } |
| if (violation.getColumn() > 0) { |
| builder.strong(", column ").strong(violation.getColumn()); |
| } |
| if (violation.getValidatorId() != null) { |
| builder.a(", validator: ").strong(violation.getValidatorId()); |
| } |
| if (violation.getNodePath() != null) { |
| builder.a(", JCR node path: ").strong(violation.getNodePath()); |
| } |
| return builder.toString(); |
| } |
| |
| private static String getValidatorNames(ValidationExecutor executor, String separator) { |
| StringBuilder validatorNames = new StringBuilder(); |
| boolean isFirstItem = true; |
| for (Map.Entry<String, Validator> validatorById : executor.getAllValidatorsById().entrySet()) { |
| if (!isFirstItem) { |
| validatorNames.append(separator); |
| } else { |
| isFirstItem = false; |
| } |
| validatorNames.append(validatorById.getKey()).append(" (").append(validatorById.getValue().getClass().getName()).append(")"); |
| } |
| return validatorNames.toString(); |
| } |
| |
| public void clearPreviousValidationMessages(BuildContext context, File file) { |
| context.removeMessages(file); |
| } |
| |
| public void failBuildInCaseOfViolations(boolean failForWarning) throws MojoFailureException { |
| if (failForWarning && (noOfEmittedValidationMessagesWithLevelWarn > 0 || noOfEmittedValidationMessagesWithLevelError > 0)) { |
| throw new MojoFailureException("Found " +noOfEmittedValidationMessagesWithLevelWarn+noOfEmittedValidationMessagesWithLevelError + " violation(s) (either ERROR or WARN). Check above warnings/errors for details"); |
| } else if (noOfEmittedValidationMessagesWithLevelError > 0) { |
| throw new MojoFailureException("Found " + noOfEmittedValidationMessagesWithLevelError + " violation(s) (with severity=ERROR). Check above errors for details"); |
| } |
| } |
| |
| private void printToCsvFile(ValidationViolation violation) throws IOException { |
| csvPrinter.printRecord(violation.getSeverity(), violation.getValidatorId(), violation.getMessage(), violation.getAbsoluteFilePath(), MessageFormat.format("{0}:{1}", violation.getLine(), violation.getColumn()), violation.getNodePath()); |
| } |
| |
| @Override |
| public void close() throws IOException { |
| if (csvPrinter != null) { |
| log.info("CSV report written to '" + csvReportFile + "'"); |
| csvPrinter.close(); |
| } |
| } |
| } |