Skip to content

Commit

Permalink
Fix #10217 quarkus-extension-processor race conditions when modules a…
Browse files Browse the repository at this point in the history
…re built in parallel
  • Loading branch information
ppalaga committed Jun 24, 2020
1 parent 9c8c2d9 commit 41718f4
Show file tree
Hide file tree
Showing 6 changed files with 237 additions and 184 deletions.
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package io.quarkus.annotation.processor;

import java.io.File;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.Duration;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
Expand Down Expand Up @@ -58,35 +58,14 @@ final public class Constants {
public static final String ANNOTATION_CONFIG_DOC_MAP_KEY = "io.quarkus.runtime.annotations.ConfigDocMapKey";
public static final String ANNOTATION_CONFIG_DOC_SECTION = "io.quarkus.runtime.annotations.ConfigDocSection";

public static final Set<String> SUPPORTED_ANNOTATIONS_TYPES = new HashSet<>();
public static final Map<String, String> ALIASED_TYPES = new HashMap<>();
public static final Set<String> SUPPORTED_ANNOTATIONS_TYPES;
public static final Map<String, String> ALIASED_TYPES;
private static final Properties SYSTEM_PROPERTIES = System.getProperties();

private static final String DOCS_SRC_MAIN_ASCIIDOC_GENERATED = "/target/asciidoc/generated/config/";
private static final String DOCS_OUT_DIR = System.getProperty("quarkus.docsOutputDir",
SYSTEM_PROPERTIES.getProperty("maven.multiModuleProjectDirectory", "."));
public static final Path GENERATED_DOCS_PATH = Paths.get(DOCS_OUT_DIR + DOCS_SRC_MAIN_ASCIIDOC_GENERATED).toAbsolutePath();
public static final File GENERATED_DOCS_DIR = GENERATED_DOCS_PATH.toFile();
public static final Boolean SKIP_DOCS_GENERATION = Boolean.valueOf(SYSTEM_PROPERTIES.getProperty("skipDocs", "false"));

/**
* Holds the list of configuration items of each configuration roots.
*/
public static final File ALL_CR_GENERATED_DOC = GENERATED_DOCS_PATH
.resolve("all-configuration-roots-generated-doc.properties").toFile();

/**
* Holds the list of configuration items of each configuration groups.
* The items in this list may not have complete information as some depend on the configuration root.
*/
public static final File ALL_CG_GENERATED_DOC = GENERATED_DOCS_PATH
.resolve("all-configuration-groups-generated-doc.properties").toFile();

/**
* Holds the list of computed file names and the list of configuration roots of this extension
*/
public static final File EXTENSION_CONFIGURATION_ROOT_LIST = GENERATED_DOCS_PATH
.resolve("extensions-configuration-roots-list.properties").toFile();

public static final String DURATION_NOTE_ANCHOR = "duration-note-anchor";
public static final String MEMORY_SIZE_NOTE_ANCHOR = "memory-size-note-anchor";
Expand Down Expand Up @@ -124,18 +103,23 @@ final public class Constants {
"====\n";

static {
ALIASED_TYPES.put(OptionalLong.class.getName(), Long.class.getName());
ALIASED_TYPES.put(OptionalInt.class.getName(), Integer.class.getName());
ALIASED_TYPES.put(OptionalDouble.class.getName(), Double.class.getName());
ALIASED_TYPES.put("java.lang.Class<?>", "class name");
ALIASED_TYPES.put("java.net.InetSocketAddress", "host:port");
ALIASED_TYPES.put(Path.class.getName(), "path");
ALIASED_TYPES.put(String.class.getName(), "string");
SUPPORTED_ANNOTATIONS_TYPES.add(ANNOTATION_BUILD_STEP);
SUPPORTED_ANNOTATIONS_TYPES.add(ANNOTATION_CONFIG_GROUP);
SUPPORTED_ANNOTATIONS_TYPES.add(ANNOTATION_CONFIG_ROOT);
SUPPORTED_ANNOTATIONS_TYPES.add(ANNOTATION_TEMPLATE);
SUPPORTED_ANNOTATIONS_TYPES.add(ANNOTATION_RECORDER);
final Map<String, String> aliasedTypes = new HashMap<>();
aliasedTypes.put(OptionalLong.class.getName(), Long.class.getName());
aliasedTypes.put(OptionalInt.class.getName(), Integer.class.getName());
aliasedTypes.put(OptionalDouble.class.getName(), Double.class.getName());
aliasedTypes.put("java.lang.Class<?>", "class name");
aliasedTypes.put("java.net.InetSocketAddress", "host:port");
aliasedTypes.put(Path.class.getName(), "path");
aliasedTypes.put(String.class.getName(), "string");
ALIASED_TYPES = Collections.unmodifiableMap(aliasedTypes);

final Set<String> supportedAnnotationTypes = new HashSet<>();
supportedAnnotationTypes.add(ANNOTATION_BUILD_STEP);
supportedAnnotationTypes.add(ANNOTATION_CONFIG_GROUP);
supportedAnnotationTypes.add(ANNOTATION_CONFIG_ROOT);
supportedAnnotationTypes.add(ANNOTATION_TEMPLATE);
supportedAnnotationTypes.add(ANNOTATION_RECORDER);
SUPPORTED_ANNOTATIONS_TYPES = Collections.unmodifiableSet(supportedAnnotationTypes);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ public class ExtensionAnnotationProcessor extends AbstractProcessor {
private final ConfigDocItemScanner configDocItemScanner = new ConfigDocItemScanner();
private final Set<String> generatedAccessors = new ConcurrentHashMap<String, Boolean>().keySet(Boolean.TRUE);
private final Set<String> generatedJavaDocs = new ConcurrentHashMap<String, Boolean>().keySet(Boolean.TRUE);
private final boolean generateDocs = !Boolean.parseBoolean(System.getProperty("skipDocs", "false"));

public ExtensionAnnotationProcessor() {
}
Expand Down Expand Up @@ -236,7 +237,7 @@ public FileVisitResult postVisitDirectory(final Path dir, final IOException exc)
}

try {
if (!Constants.SKIP_DOCS_GENERATION) {
if (generateDocs) {
final Set<ConfigDocGeneratedOutput> outputs = configDocItemScanner
.scanExtensionsConfigurationItems(javaDocProperties);
for (ConfigDocGeneratedOutput output : outputs) {
Expand Down Expand Up @@ -412,7 +413,9 @@ private void processConfigGroup(RoundEnvironment roundEnv, TypeElement annotatio
if (groupClassNames.add(i.getQualifiedName().toString())) {
generateAccessor(i);
recordConfigJavadoc(i);
configDocItemScanner.addConfigGroups(i);
if (generateDocs) {
configDocItemScanner.addConfigGroups(i);
}
}
}
}
Expand All @@ -428,7 +431,9 @@ private void processConfigRoot(RoundEnvironment roundEnv, TypeElement annotation
continue;
}

configDocItemScanner.addConfigRoot(pkg, clazz);
if (generateDocs) {
configDocItemScanner.addConfigRoot(pkg, clazz);
}

final String binaryName = processingEnv.getElementUtils().getBinaryName(clazz).toString();
if (rootClassNames.add(binaryName)) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package io.quarkus.annotation.processor.generate_doc;

import static io.quarkus.annotation.processor.Constants.ALL_CG_GENERATED_DOC;
import static io.quarkus.annotation.processor.Constants.ANNOTATION_CONFIG_DOC_MAP_KEY;
import static io.quarkus.annotation.processor.Constants.ANNOTATION_CONFIG_DOC_SECTION;
import static io.quarkus.annotation.processor.Constants.ANNOTATION_CONFIG_ITEM;
Expand All @@ -19,13 +18,11 @@
import static io.quarkus.annotation.processor.generate_doc.DocGeneratorUtil.hyphenateEnumValue;
import static io.quarkus.annotation.processor.generate_doc.DocGeneratorUtil.stringifyType;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
Expand Down Expand Up @@ -55,50 +52,41 @@ class ConfigDoItemFinder {
private static String COMMA = ",";
private static final String BACK_TICK = "`";
private static final String NAMED_MAP_CONFIG_ITEM_FORMAT = ".\"%s\"";
private static final Set<String> PRIMITIVE_TYPES = new HashSet<>(
Arrays.asList("byte", "short", "int", "long", "float", "double", "boolean", "char"));

private final JavaDocParser javaDocParser = new JavaDocParser();
private final ScannedConfigDocsItemHolder holder = new ScannedConfigDocsItemHolder();

private final Set<ConfigRootInfo> configRoots;
private final Properties javaDocProperties;
private final Properties configGroupDocs = new Properties();
private final Map<String, TypeElement> configGroupQualifiedNameToTypeElementMap;
private final FsMap allConfigurationGroups;

public ConfigDoItemFinder(Set<ConfigRootInfo> configRoots,
Map<String, TypeElement> configGroupQualifiedNameToTypeElementMap,
Properties javaDocProperties) {
Properties javaDocProperties, Path allConfigurationGroupsDir) {
this.configRoots = configRoots;
this.configGroupQualifiedNameToTypeElementMap = configGroupQualifiedNameToTypeElementMap;
this.javaDocProperties = javaDocProperties;
this.allConfigurationGroups = new FsMap(allConfigurationGroupsDir);
}

/**
* Find configuration items from current encountered configuration roots.
* Scan configuration group first and record them in a properties file as they can be shared across
* different modules.
*
* @param allConfigurationGroups
*/
ScannedConfigDocsItemHolder findInMemoryConfigurationItems() throws IOException {
if (ALL_CG_GENERATED_DOC.exists()) {
try (BufferedReader bufferedReader = Files.newBufferedReader(ALL_CG_GENERATED_DOC.toPath(),
StandardCharsets.UTF_8)) {
configGroupDocs.load(bufferedReader);
}
}

for (Map.Entry<String, TypeElement> entry : configGroupQualifiedNameToTypeElementMap.entrySet()) {
ConfigPhase buildTime = ConfigPhase.BUILD_TIME;
final List<ConfigDocItem> configDocItems = recursivelyFindConfigItems(
entry.getValue(), EMPTY, EMPTY, buildTime,
false, 1, false);
configGroupDocs.put(entry.getKey(), OBJECT_MAPPER.writeValueAsString(configDocItems));
}

/**
* Update stored generated config group doc for each configuration
*/
try (BufferedWriter bufferedWriter = Files.newBufferedWriter(ALL_CG_GENERATED_DOC.toPath(),
StandardCharsets.UTF_8)) {
configGroupDocs.store(bufferedWriter, EMPTY);
allConfigurationGroups.put(entry.getKey(), OBJECT_MAPPER.writeValueAsString(configDocItems));
}

for (ConfigRootInfo configRootInfo : configRoots) {
Expand Down Expand Up @@ -327,7 +315,10 @@ private List<ConfigDocItem> recursivelyFindConfigItems(Element element, String r
}

private boolean isConfigGroup(String type) {
return configGroupDocs.containsKey(type) || configGroupQualifiedNameToTypeElementMap.containsKey(type);
if (type.startsWith("java.") || PRIMITIVE_TYPES.contains(type)) {
return false;
}
return configGroupQualifiedNameToTypeElementMap.containsKey(type) || allConfigurationGroups.hasKey(type);
}

private String simpleTypeToString(TypeMirror typeMirror) {
Expand Down Expand Up @@ -376,7 +367,7 @@ private boolean isEnumType(TypeMirror realTypeMirror) {
* If the configuration group is already scanned, retrieve the scanned items and parse them
* If not, make sure that items of a given configuration group are properly scanned and the record of scanned
* configuration group if properly updated afterwards.
*
*
*/
private List<ConfigDocItem> readConfigGroupItems(ConfigPhase configPhase, String topLevelRootName, String parentName,
String configGroup, ConfigDocSection configSection, boolean withinAMap,
Expand All @@ -392,23 +383,23 @@ private List<ConfigDocItem> readConfigGroupItems(ConfigPhase configPhase, String
}

final List<ConfigDocItem> configDocItems = new ArrayList<>();
String property = configGroupDocs.getProperty(configGroup);
String property = allConfigurationGroups.get(configGroup);
List<ConfigDocItem> groupConfigItems;
if (property != null) {
groupConfigItems = OBJECT_MAPPER.readValue(property, LIST_OF_CONFIG_ITEMS_TYPE_REF);
} else {
TypeElement configGroupTypeElement = configGroupQualifiedNameToTypeElementMap.get(configGroup);
groupConfigItems = recursivelyFindConfigItems(configGroupTypeElement, EMPTY, EMPTY, configPhase,
false, 1, generateSeparateConfigGroupDocs);
configGroupDocs.put(configGroup, OBJECT_MAPPER.writeValueAsString(groupConfigItems));
allConfigurationGroups.put(configGroup, OBJECT_MAPPER.writeValueAsString(groupConfigItems));
}

groupConfigItems = decorateGroupItems(groupConfigItems, configPhase, topLevelRootName, parentName, withinAMap,
generateSeparateConfigGroupDocs);

// make sure that the config section is added if it is to be shown or when scanning parent configuration group
// priory to scanning configuration roots. This is useful as we get indication of whether the config items are part
// of a configuration section (i.e configuration group) we are current scanning.
// make sure that the config section is added if it is to be shown or when scanning parent configuration group
// priory to scanning configuration roots. This is useful as we get indication of whether the config items are part
// of a configuration section (i.e configuration group) we are current scanning.
if (configSection.isShowSection() || !generateSeparateConfigGroupDocs) {
final ConfigDocItem configDocItem = new ConfigDocItem();
configDocItem.setConfigDocSection(configSection);
Expand Down
Loading

0 comments on commit 41718f4

Please sign in to comment.