blob: 01640f1c30ab011e13608bbebe84d34cd138e8a6 [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.dubbo.errorcode;
import org.apache.dubbo.errorcode.config.ErrorCodeInspectorConfig;
import org.apache.dubbo.errorcode.extractor.ErrorCodeExtractor;
import org.apache.dubbo.errorcode.extractor.InvalidLoggerInvocationLocator;
import org.apache.dubbo.errorcode.extractor.JavassistConstantPoolErrorCodeExtractor;
import org.apache.dubbo.errorcode.extractor.JdtBasedInvalidLoggerInvocationLocator;
import org.apache.dubbo.errorcode.linktest.LinkTestingForkJoinTask;
import org.apache.dubbo.errorcode.model.LoggerMethodInvocation;
import org.apache.dubbo.errorcode.model.MethodDefinition;
import org.apache.dubbo.errorcode.reporter.InspectionResult;
import org.apache.dubbo.errorcode.util.ErrorCodeStringComparator;
import org.apache.dubbo.errorcode.util.FileUtils;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
/**
* Error code extractor main class.
*/
public class Main {
private static final ThreadPoolExecutor EXECUTOR = new ThreadPoolExecutor(16, 32, 2, TimeUnit.MINUTES, new LinkedBlockingQueue<>());
private static final ErrorCodeExtractor ERROR_CODE_EXTRACTOR = new JavassistConstantPoolErrorCodeExtractor();
private static final InvalidLoggerInvocationLocator INVALID_LOGGER_INVOCATION_LOCATOR = new JdtBasedInvalidLoggerInvocationLocator();
private static String directoryToInspect;
public static void main(String[] args) {
directoryToInspect = args[0];
System.out.println("Directory to inspect: " + directoryToInspect);
// Step 1
System.out.println("Scanning error codes and detecting invalid logger invocation...");
long millis1 = System.currentTimeMillis();
List<Path> targetFolders = FileUtils.getAllClassFilePaths(args[0]);
Map<Path, List<String>> fileBasedCodes = new HashMap<>(1024);
List<String> codes = Collections.synchronizedList(new ArrayList<>(30));
Map<String, List<MethodDefinition>> illegalLoggerMethodInvocations = new ConcurrentHashMap<>(256);
CountDownLatch countDownLatch = new CountDownLatch(targetFolders.size());
for (Path folder : targetFolders) {
EXECUTOR.submit(() -> handleSinglePackageFolder(
fileBasedCodes,
codes,
illegalLoggerMethodInvocations,
countDownLatch,
folder));
}
try {
countDownLatch.await();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException(e);
}
long millis2 = System.currentTimeMillis();
System.out.println("Milliseconds elapsed: " + (millis2 - millis1));
// Step 2
System.out.println("Locating illegal logger method invocations...");
millis1 = System.currentTimeMillis();
Map<String, List<MethodDefinition>> illegalInvocationClassesAndLoggerMethods = illegalLoggerMethodInvocations.entrySet()
.stream()
.filter(e -> !e.getValue().isEmpty())
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
Map<String, List<LoggerMethodInvocation>> invalidLoggerMethodInvocationLocations = new HashMap<>();
Set<String> illegalInvocationClasses = illegalInvocationClassesAndLoggerMethods.keySet();
illegalInvocationClasses.forEach(x ->
invalidLoggerMethodInvocationLocations.put(
x, INVALID_LOGGER_INVOCATION_LOCATOR.locateInvalidLoggerInvocation(x)
)
);
millis2 = System.currentTimeMillis();
System.out.println("Milliseconds elapsed: " + (millis2 - millis1));
// Step 3
System.out.println("Finding error codes that document links are not reachable...");
millis1 = System.currentTimeMillis();
List<String> linksNotReachable = LinkTestingForkJoinTask.findDocumentMissingErrorCodes(codes);
millis2 = System.currentTimeMillis();
System.out.println("Milliseconds elapsed: " + (millis2 - millis1));
System.out.println();
InspectionResult inspectionResult = getInspectionResult(
codes,
illegalInvocationClassesAndLoggerMethods,
invalidLoggerMethodInvocationLocations,
linksNotReachable);
ErrorCodeInspectorConfig.REPORTERS.forEach(x -> x.report(inspectionResult));
cleanUp();
if (ErrorCodeInspectorConfig.REPORT_AS_ERROR) {
if (!inspectionResult.getIllegalInvocations().isEmpty() ||
!inspectionResult.getLinkNotReachableErrorCodes().isEmpty()) {
throw new IllegalStateException("Invalid situation occurred, check console or log for details;");
}
} else {
System.out.println("Tolerance mode enabled, will not throw exception.");
}
}
private static InspectionResult getInspectionResult(List<String> codes, Map<String, List<MethodDefinition>> illegalInvocationClassesAndLoggerMethods, Map<String, List<LoggerMethodInvocation>> invalidLoggerMethodInvocationLocations, List<String> linksNotReachable) {
InspectionResult inspectionResult = new InspectionResult();
inspectionResult.setAllErrorCodes(
codes.stream()
.distinct()
.sorted(ErrorCodeStringComparator.getInstance())
.collect(Collectors.toList())
);
inspectionResult.setLinkNotReachableErrorCodes(
linksNotReachable.stream()
.distinct()
.sorted(ErrorCodeStringComparator.getInstance())
.collect(Collectors.toList())
);
inspectionResult.setIllegalInvocations(illegalInvocationClassesAndLoggerMethods);
inspectionResult.setInvalidLoggerMethodInvocationLocations(invalidLoggerMethodInvocationLocations);
return inspectionResult;
}
private static void handleSinglePackageFolder(Map<Path, List<String>> fileBasedCodes,
List<String> codes,
Map<String, List<MethodDefinition>> illegalLoggerMethodInvocation,
CountDownLatch countDownLatch,
Path folder) {
try {
List<Path> classFiles = FileUtils.getAllClassFilesInDirectory(Paths.get(directoryToInspect), folder);
classFiles.forEach(x -> {
List<String> fileBasedCodesErrorCodeList = new ArrayList<>(4);
List<String> extractedCodeList = ERROR_CODE_EXTRACTOR.getErrorCodes(x.toString());
illegalLoggerMethodInvocation.put(x.toString(), ERROR_CODE_EXTRACTOR.getIllegalLoggerMethodInvocations(x.toString()));
if (!extractedCodeList.isEmpty()) {
fileBasedCodesErrorCodeList.addAll(extractedCodeList.stream().distinct().collect(Collectors.toList()));
fileBasedCodes.put(x, fileBasedCodesErrorCodeList);
codes.addAll(extractedCodeList);
}
});
} finally {
countDownLatch.countDown();
}
}
private static void cleanUp() {
EXECUTOR.shutdown();
LinkTestingForkJoinTask.closeHttpClient();
}
}