🐛 Fixing some issues with threading and NPE (#5107)

* 🐛 Fixing some issues with threading and NPE

After running Sonar on the master branch, some major analysis
opportunities were displayed.

This fixes the use of SimpleDateFormat stored as static fields.
SimpleDateFormat is not thread-safe, and may retain data across threads.
While there's no indicator that this has caused any issues (these are
mostly used for example code), we should follow these best practices.

This also fixes a handful of NPE and other minor issues such as
comparing Boolean.TRUE to strings and no wrapping some closeables in
try-with-resources.

* [cli] Unit test GenerateBatch custom deserialization helper

* Quiet batch mode in sonar.yml

* Suppress unnecessary warnings (ThreadLocals in static fields)
This commit is contained in:
Jim Schubert
2020-01-25 18:28:16 -05:00
committed by GitHub
parent ad4a9df328
commit c9ec084418
27 changed files with 380 additions and 110 deletions

View File

@@ -123,27 +123,12 @@ public class GenerateBatch implements Runnable {
}
}
LOGGER.info(String.format(Locale.ROOT, "Batch generation using up to %d threads.\nIncludes: %s\nRoot: %s", numThreads, includesDir.getAbsolutePath(), rootDir.toAbsolutePath().toString()));
// Create a module which loads our config files, but supports a special "!include" key which can point to an existing config file.
// This allows us to create a sort of meta-config which holds configs which are otherwise required at CLI time (via generate task).
// That is, this allows us to create a wrapper config for generatorName, inputSpec, outputDir, etc.
SimpleModule module = new SimpleModule("GenerateBatch");
module.setDeserializerModifier(new BeanDeserializerModifier() {
@Override
public JsonDeserializer<?> modifyDeserializer(DeserializationConfig config,
BeanDescription bd, JsonDeserializer<?> original) {
JsonDeserializer<?> result;
if (bd.getBeanClass() == DynamicSettings.class) {
result = new DynamicSettingsRefSupport(original, includesDir);
} else {
result = original;
}
return result;
}
});
SimpleModule module = getCustomDeserializationModel(includesDir);
List<CodegenConfigurator> configurators = configs.stream().map(config -> CodegenConfigurator.fromFile(config, module)).collect(Collectors.toList());
// it doesn't make sense to interleave INFO level logs, so limit these to only ERROR.
@@ -169,6 +154,8 @@ public class GenerateBatch implements Runnable {
System.out.println("COMPLETE.");
} catch (InterruptedException e) {
e.printStackTrace();
// re-interrupt
Thread.currentThread().interrupt();
}
}
@@ -227,6 +214,28 @@ public class GenerateBatch implements Runnable {
}
}
static SimpleModule getCustomDeserializationModel(final File includesDir) {
// Create a module which loads our config files, but supports a special "!include" key which can point to an existing config file.
// This allows us to create a sort of meta-config which holds configs which are otherwise required at CLI time (via generate task).
// That is, this allows us to create a wrapper config for generatorName, inputSpec, outputDir, etc.
SimpleModule module = new SimpleModule("GenerateBatch");
module.setDeserializerModifier(new BeanDeserializerModifier() {
@Override
public JsonDeserializer<?> modifyDeserializer(DeserializationConfig config,
BeanDescription bd, JsonDeserializer<?> original) {
JsonDeserializer<?> result;
if (bd.getBeanClass() == DynamicSettings.class) {
result = new DynamicSettingsRefSupport(original, includesDir);
} else {
result = original;
}
return result;
}
});
return module;
}
static class DynamicSettingsRefSupport extends DelegatingDeserializer {
private static final String INCLUDE = "!include";
private File scanDir;
@@ -255,11 +264,13 @@ public class GenerateBatch implements Runnable {
// load the file into the tree node and continue parsing as normal
((ObjectNode) node).remove(INCLUDE);
JsonParser includeParser = codec.getFactory().createParser(includeFile);
TreeNode includeNode = includeParser.readValueAsTree();
TreeNode includeNode;
try (JsonParser includeParser = codec.getFactory().createParser(includeFile)) {
includeNode = includeParser.readValueAsTree();
}
ObjectReader reader = codec.readerForUpdating(node);
TreeNode updated = reader.readValue(includeFile);
TreeNode updated = reader.readValue(includeNode.traverse());
JsonParser updatedParser = updated.traverse();
updatedParser.nextToken();
return super.deserialize(updatedParser, ctx);