blob: 0985395fec51937fd134f136e89eb8848b30c9de [file] [log] [blame]
/*
* 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();
}
}
}