From 5776deaab447a70775558dd5d84bc30cec8d5181 Mon Sep 17 00:00:00 2001 From: Michael Dowling Date: Thu, 18 Aug 2022 16:35:50 -0700 Subject: [PATCH] Cleanup smithy-build * Removes the need to create intermediate SmithyBuildConfig classes when loading configurations from JSON files. * Only perform variable expansion if it's used. * Adds fromNode for smithy-build classes. * Removes several instances of Stream that are called in hot paths / don't need stream overhead. * Switch from custom ForkJoinPool to parallel stream. There's no measurable difference here and this approach is simpler. --- .../amazon/smithy/build/ProjectionResult.java | 7 +- .../amazon/smithy/build/SmithyBuildImpl.java | 52 ++--------- .../smithy/build/SmithyBuildPlugin.java | 11 ++- .../smithy/build/SmithyBuildResult.java | 23 +++-- .../smithy/build/model/ProjectionConfig.java | 11 ++- .../smithy/build/model/SmithyBuildConfig.java | 62 ++++++++----- ...onfigLoader.java => SmithyBuildUtils.java} | 89 +++++++++---------- .../smithy/build/plugins/BuildInfoPlugin.java | 38 +++++--- .../amazon/smithy/build/SmithyBuildTest.java | 18 ++-- .../smithy/build/Test1ParallelPlugin.java | 2 +- .../smithy/build/Test1SerialPlugin.java | 12 +-- .../smithy/build/Test2ParallelPlugin.java | 2 +- .../smithy/build/Test2SerialPlugin.java | 12 +-- .../build/model/SmithyBuildConfigTest.java | 31 +++++++ .../smithy/build/model/loads-from-node.json | 9 ++ 15 files changed, 211 insertions(+), 168 deletions(-) rename smithy-build/src/main/java/software/amazon/smithy/build/model/{ConfigLoader.java => SmithyBuildUtils.java} (57%) create mode 100644 smithy-build/src/test/resources/software/amazon/smithy/build/model/loads-from-node.json diff --git a/smithy-build/src/main/java/software/amazon/smithy/build/ProjectionResult.java b/smithy-build/src/main/java/software/amazon/smithy/build/ProjectionResult.java index b19222a4ded..836a3b2d05b 100644 --- a/smithy-build/src/main/java/software/amazon/smithy/build/ProjectionResult.java +++ b/smithy-build/src/main/java/software/amazon/smithy/build/ProjectionResult.java @@ -74,7 +74,12 @@ public Model getModel() { * @return Returns true if the projected model is broken. */ public boolean isBroken() { - return events.stream().anyMatch(e -> e.getSeverity() == Severity.ERROR || e.getSeverity() == Severity.DANGER); + for (ValidationEvent e : events) { + if (e.getSeverity() == Severity.ERROR || e.getSeverity() == Severity.DANGER) { + return true; + } + } + return false; } /** diff --git a/smithy-build/src/main/java/software/amazon/smithy/build/SmithyBuildImpl.java b/smithy-build/src/main/java/software/amazon/smithy/build/SmithyBuildImpl.java index a5cc068753b..b57934824d7 100644 --- a/smithy-build/src/main/java/software/amazon/smithy/build/SmithyBuildImpl.java +++ b/smithy-build/src/main/java/software/amazon/smithy/build/SmithyBuildImpl.java @@ -27,11 +27,6 @@ import java.util.Optional; import java.util.Set; import java.util.TreeMap; -import java.util.concurrent.Callable; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.ForkJoinPool; -import java.util.concurrent.Future; import java.util.function.BiConsumer; import java.util.function.Consumer; import java.util.function.Function; @@ -163,8 +158,7 @@ void applyAllProjections( // The projections are being split up here because we need to be able // to break out non-parallelizeable plugins. Right now the only // parallelization that occurs is at the projection level. - List> parallelProjections = new ArrayList<>(); - List parallelProjectionNameOrder = new ArrayList<>(); + List parallelProjections = new ArrayList<>(); for (Map.Entry entry : config.getProjections().entrySet()) { String name = entry.getKey(); @@ -184,28 +178,19 @@ void applyAllProjections( executeSerialProjection(resolvedModel, name, config, projectionResultConsumer, projectionExceptionConsumer); } else { - parallelProjectionNameOrder.add(name); parallelProjections.add(() -> { executeSerialProjection(resolvedModel, name, config, projectionResultConsumer, projectionExceptionConsumer); - return null; }); } } - // Common case of only executing a single plugin per/projection. - if (parallelProjections.size() == 1) { - try { - parallelProjections.get(0).call(); - } catch (Throwable e) { - if (e instanceof RuntimeException) { - throw (RuntimeException) e; - } else { - throw new RuntimeException(e); - } + if (!parallelProjections.isEmpty()) { + if (parallelProjections.size() == 1) { + parallelProjections.get(0).run(); + } else { + parallelProjections.parallelStream().forEach(Runnable::run); } - } else if (!parallelProjections.isEmpty()) { - executeParallelProjections(parallelProjections, parallelProjectionNameOrder, projectionExceptionConsumer); } } @@ -231,31 +216,6 @@ private void executeSerialProjection( } } - private void executeParallelProjections( - List> parallelProjections, - List parallelProjectionNameOrder, - BiConsumer projectionExceptionConsumer - ) { - ExecutorService executor = ForkJoinPool.commonPool(); - - try { - List> futures = executor.invokeAll(parallelProjections); - // Futures are returned in the same order they were added, so - // use the list of ordered names to know which projections failed. - for (int i = 0; i < futures.size(); i++) { - try { - futures.get(i).get(); - } catch (ExecutionException e) { - Throwable cause = e.getCause() != null ? e.getCause() : e; - String failedProjectionName = parallelProjectionNameOrder.get(i); - projectionExceptionConsumer.accept(failedProjectionName, cause); - } - } - } catch (InterruptedException e) { - throw new SmithyBuildException(e.getMessage(), e); - } - } - private ValidatedResult createBaseModel() { if (!config.getImports().isEmpty()) { LOGGER.fine(() -> "Merging the following imports into the loaded model: " + config.getImports()); diff --git a/smithy-build/src/main/java/software/amazon/smithy/build/SmithyBuildPlugin.java b/smithy-build/src/main/java/software/amazon/smithy/build/SmithyBuildPlugin.java index 218d830a1c1..27db5646549 100644 --- a/smithy-build/src/main/java/software/amazon/smithy/build/SmithyBuildPlugin.java +++ b/smithy-build/src/main/java/software/amazon/smithy/build/SmithyBuildPlugin.java @@ -82,9 +82,14 @@ static Function> createServiceFactory(Iterab // caching a ServiceLoader using a Thread's context ClassLoader VM-wide. List pluginList = new ArrayList<>(); plugins.forEach(pluginList::add); - return name -> pluginList.stream() - .filter(plugin -> plugin.getName().equals(name)) - .findFirst(); + return name -> { + for (SmithyBuildPlugin plugin : pluginList) { + if (plugin.getName().equals(name)) { + return Optional.of(plugin); + } + } + return Optional.empty(); + }; } /** diff --git a/smithy-build/src/main/java/software/amazon/smithy/build/SmithyBuildResult.java b/smithy-build/src/main/java/software/amazon/smithy/build/SmithyBuildResult.java index 534a2e0f955..e9d80bd3688 100644 --- a/smithy-build/src/main/java/software/amazon/smithy/build/SmithyBuildResult.java +++ b/smithy-build/src/main/java/software/amazon/smithy/build/SmithyBuildResult.java @@ -18,11 +18,10 @@ import java.nio.file.Path; import java.util.ArrayList; import java.util.Collections; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; -import java.util.function.Function; -import java.util.stream.Collectors; import java.util.stream.Stream; import software.amazon.smithy.utils.ListUtils; import software.amazon.smithy.utils.SmithyBuilder; @@ -52,7 +51,12 @@ public static Builder builder() { * @return Returns true if any are broken. */ public boolean anyBroken() { - return results.stream().anyMatch(ProjectionResult::isBroken); + for (ProjectionResult result : results) { + if (result.isBroken()) { + return true; + } + } + return false; } /** @@ -80,7 +84,12 @@ public Stream allManifests() { * @return Returns the optionally found result. */ public Optional getProjectionResult(String projectionName) { - return results.stream().filter(result -> result.getProjectionName().equals(projectionName)).findFirst(); + for (ProjectionResult result : results) { + if (result.getProjectionName().equals(projectionName)) { + return Optional.of(result); + } + } + return Optional.empty(); } /** @@ -99,7 +108,11 @@ public List getProjectionResults() { * @return Returns the projection results as a map. */ public Map getProjectionResultsMap() { - return results.stream().collect(Collectors.toMap(ProjectionResult::getProjectionName, Function.identity())); + Map resultMap = new HashMap<>(); + for (ProjectionResult result : results) { + resultMap.put(result.getProjectionName(), result); + } + return Collections.unmodifiableMap(resultMap); } /** diff --git a/smithy-build/src/main/java/software/amazon/smithy/build/model/ProjectionConfig.java b/smithy-build/src/main/java/software/amazon/smithy/build/model/ProjectionConfig.java index 4db502fbc65..30c4bc52063 100644 --- a/smithy-build/src/main/java/software/amazon/smithy/build/model/ProjectionConfig.java +++ b/smithy-build/src/main/java/software/amazon/smithy/build/model/ProjectionConfig.java @@ -15,13 +15,13 @@ package software.amazon.smithy.build.model; +import java.nio.file.Path; import java.util.Collection; import java.util.List; import java.util.Map; import software.amazon.smithy.build.SmithyBuildException; import software.amazon.smithy.model.node.Node; import software.amazon.smithy.model.node.ObjectNode; -import software.amazon.smithy.model.node.StringNode; import software.amazon.smithy.utils.BuilderRef; import software.amazon.smithy.utils.SmithyBuilder; import software.amazon.smithy.utils.ToSmithyBuilder; @@ -60,10 +60,15 @@ public Builder toBuilder() { } public static ProjectionConfig fromNode(Node node) { + return fromNode(node, SmithyBuildUtils.getBasePathFromSourceLocation(node)); + } + + static ProjectionConfig fromNode(Node node, Path basePath) { Builder builder = ProjectionConfig.builder(); node.expectObjectNode() .getBooleanMember("abstract", builder::setAbstract) - .getArrayMember("imports", StringNode::getValue, builder::imports) + .getArrayMember("imports", s -> SmithyBuildUtils.resolveImportPath(basePath, s), + builder::imports) .getArrayMember("transforms", TransformConfig::fromNode, builder::transforms) .getObjectMember("plugins", plugins -> { for (Map.Entry entry : plugins.getStringMap().entrySet()) { @@ -103,8 +108,6 @@ public List getImports() { return imports; } - - /** * Builds a {@link ProjectionConfig}. */ diff --git a/smithy-build/src/main/java/software/amazon/smithy/build/model/SmithyBuildConfig.java b/smithy-build/src/main/java/software/amazon/smithy/build/model/SmithyBuildConfig.java index a32af3bce4d..717f1b3c493 100644 --- a/smithy-build/src/main/java/software/amazon/smithy/build/model/SmithyBuildConfig.java +++ b/smithy-build/src/main/java/software/amazon/smithy/build/model/SmithyBuildConfig.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -16,16 +16,19 @@ package software.amazon.smithy.build.model; import java.nio.file.Path; +import java.nio.file.Paths; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; +import software.amazon.smithy.build.SmithyBuildException; +import software.amazon.smithy.model.loader.ModelSyntaxException; import software.amazon.smithy.model.node.Node; import software.amazon.smithy.model.node.ObjectNode; -import software.amazon.smithy.model.node.StringNode; import software.amazon.smithy.utils.BuilderRef; +import software.amazon.smithy.utils.IoUtils; import software.amazon.smithy.utils.SetUtils; import software.amazon.smithy.utils.SmithyBuilder; import software.amazon.smithy.utils.ToSmithyBuilder; @@ -55,23 +58,10 @@ private SmithyBuildConfig(Builder builder) { } public static SmithyBuildConfig fromNode(Node node) { - Builder builder = builder(); - node.expectObjectNode() - .expectStringMember("version", builder::version) - .getStringMember("outputDirectory", builder::outputDirectory) - .getArrayMember("imports", StringNode::getValue, builder::imports) - .getObjectMember("projections", v -> { - for (Map.Entry entry : v.getStringMap().entrySet()) { - builder.projections.get().put(entry.getKey(), ProjectionConfig.fromNode(entry.getValue())); - } - }) - .getObjectMember("plugins", v -> { - for (Map.Entry entry : v.getStringMap().entrySet()) { - builder.plugins.get().put(entry.getKey(), entry.getValue().expectObjectNode()); - } - }) - .getBooleanMember("ignoreMissingPlugins", builder::ignoreMissingPlugins); - return builder.build(); + Path path = SmithyBuildUtils.getBasePathFromSourceLocation(node); + // Expand variables before deserializing the node into the builder. + ObjectNode expanded = SmithyBuildUtils.expandNode(node); + return builder().loadNode(path, expanded).build(); } /** @@ -85,7 +75,6 @@ public static Builder builder() { * Loads a SmithyBuildConfig from a JSON file on disk. * *

The file is expected to contain the following structure: - * * * { * "version": "1.0", @@ -232,7 +221,38 @@ public Builder version(String version) { * @return Returns the updated builder. */ public Builder load(Path config) { - return merge(ConfigLoader.load(config)); + try { + String content = IoUtils.readUtf8File(config); + Path basePath = config.getParent(); + if (basePath == null) { + basePath = Paths.get("."); + } + Node loadedAndExpanded = SmithyBuildUtils.loadAndExpandJson(config.toString(), content); + return loadNode(basePath, loadedAndExpanded); + } catch (ModelSyntaxException e) { + throw new SmithyBuildException(e); + } + } + + private Builder loadNode(Path basePath, Node node) { + node.expectObjectNode() + .expectStringMember("version", this::version) + .getStringMember("outputDirectory", this::outputDirectory) + .getArrayMember("imports", s -> SmithyBuildUtils.resolveImportPath(basePath, s), + values -> imports.get().addAll(values)) + .getObjectMember("projections", v -> { + for (Map.Entry entry : v.getStringMap().entrySet()) { + projections.get().put(entry.getKey(), ProjectionConfig + .fromNode(entry.getValue(), basePath)); + } + }) + .getObjectMember("plugins", v -> { + for (Map.Entry entry : v.getStringMap().entrySet()) { + plugins.get().put(entry.getKey(), entry.getValue().expectObjectNode()); + } + }) + .getBooleanMember("ignoreMissingPlugins", this::ignoreMissingPlugins); + return this; } /** diff --git a/smithy-build/src/main/java/software/amazon/smithy/build/model/ConfigLoader.java b/smithy-build/src/main/java/software/amazon/smithy/build/model/SmithyBuildUtils.java similarity index 57% rename from smithy-build/src/main/java/software/amazon/smithy/build/model/ConfigLoader.java rename to smithy-build/src/main/java/software/amazon/smithy/build/model/SmithyBuildUtils.java index 034e000c761..1d201b450db 100644 --- a/smithy-build/src/main/java/software/amazon/smithy/build/model/ConfigLoader.java +++ b/smithy-build/src/main/java/software/amazon/smithy/build/model/SmithyBuildUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -17,71 +17,57 @@ import java.nio.file.Path; import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.regex.Matcher; import java.util.regex.Pattern; -import java.util.stream.Collectors; import software.amazon.smithy.build.SmithyBuildException; +import software.amazon.smithy.model.FromSourceLocation; import software.amazon.smithy.model.SourceLocation; -import software.amazon.smithy.model.loader.ModelSyntaxException; import software.amazon.smithy.model.node.ArrayNode; import software.amazon.smithy.model.node.Node; import software.amazon.smithy.model.node.NodeVisitor; import software.amazon.smithy.model.node.ObjectNode; import software.amazon.smithy.model.node.StringNode; -import software.amazon.smithy.utils.IoUtils; -import software.amazon.smithy.utils.Pair; -/** - * Loads a {@link SmithyBuildConfig} from disk. - */ -final class ConfigLoader { - - private ConfigLoader() {} +final class SmithyBuildUtils { - static SmithyBuildConfig load(Path path) { - try { - String content = IoUtils.readUtf8File(path); - Path baseImportPath = path.getParent(); - if (baseImportPath == null) { - baseImportPath = Paths.get("."); - } - return load(baseImportPath, loadWithJson(path, content).expectObjectNode()); - } catch (ModelSyntaxException e) { - throw new SmithyBuildException(e); - } - } + private SmithyBuildUtils() {} - private static Node loadWithJson(Path path, String contents) { - return Node.parseJsonWithComments(contents, path.toString()).accept(new VariableExpander()); + static String resolveImportPath(Path basePath, Node node) { + String value = node.expectStringNode().getValue(); + return basePath == null ? value : basePath.resolve(value).toString(); } - private static SmithyBuildConfig load(Path baseImportPath, ObjectNode node) { - return resolveImports(baseImportPath, SmithyBuildConfig.fromNode(node)); - } + static ObjectNode loadAndExpandJson(String path, String contents) { + Node result = Node.parseJsonWithComments(contents, path); - private static SmithyBuildConfig resolveImports(Path baseImportPath, SmithyBuildConfig config) { - List imports = config.getImports().stream() - .map(importPath -> baseImportPath.resolve(importPath).toString()) - .collect(Collectors.toList()); + // No need to expand variables if they aren't used. + if (contents.contains("${")) { + result = result.accept(new VariableExpander()); + } - Map projections = config.getProjections().entrySet().stream() - .map(entry -> Pair.of(entry.getKey(), resolveProjectionImports(baseImportPath, entry.getValue()))) - .collect(Collectors.toMap(Pair::getKey, Pair::getValue)); + return result.expectObjectNode(); + } - return config.toBuilder() - .imports(imports) - .projections(projections) - .build(); + static Path getBasePathFromSourceLocation(FromSourceLocation fromSourceLocation) { + SourceLocation sourceLocation = fromSourceLocation.getSourceLocation(); + // Attempt to resolve a path based on the given Node. + Path path = null; + if (sourceLocation != SourceLocation.NONE) { + path = Paths.get(sourceLocation.getFilename()).getParent(); + } + if (path == null) { + path = Paths.get("."); + } + return path; } - private static ProjectionConfig resolveProjectionImports(Path baseImportPath, ProjectionConfig config) { - List imports = config.getImports().stream() - .map(importPath -> baseImportPath.resolve(importPath).toString()) - .collect(Collectors.toList()); - return config.toBuilder().imports(imports).build(); + static ObjectNode expandNode(Node node) { + return node.accept(new VariableExpander()).expectObjectNode(); } /** @@ -100,18 +86,25 @@ protected Node getDefault(Node node) { @Override public Node arrayNode(ArrayNode node) { - return node.getElements().stream().map(element -> element.accept(this)).collect(ArrayNode.collect()); + List result = new ArrayList<>(node.size()); + for (Node element : node.getElements()) { + result.add(element.accept(this)); + } + return new ArrayNode(result, node.getSourceLocation()); } @Override public Node objectNode(ObjectNode node) { - return node.getMembers().entrySet().stream() - .map(entry -> Pair.of(entry.getKey().accept(this), entry.getValue().accept(this))) - .collect(ObjectNode.collect(pair -> pair.getLeft().expectStringNode(), Pair::getRight)); + Map result = new LinkedHashMap<>(node.size()); + for (Map.Entry entry : node.getMembers().entrySet()) { + result.put(entry.getKey().accept(this).expectStringNode(), entry.getValue().accept(this)); + } + return new ObjectNode(result, node.getSourceLocation()); } @Override public Node stringNode(StringNode node) { + // TODO: Update this to make a single pass over the string rather than use multiple regular expressions. Matcher matcher = INLINE.matcher(node.getValue()); StringBuffer builder = new StringBuffer(); diff --git a/smithy-build/src/main/java/software/amazon/smithy/build/plugins/BuildInfoPlugin.java b/smithy-build/src/main/java/software/amazon/smithy/build/plugins/BuildInfoPlugin.java index 72babdb2776..83a28ebd15e 100644 --- a/smithy-build/src/main/java/software/amazon/smithy/build/plugins/BuildInfoPlugin.java +++ b/smithy-build/src/main/java/software/amazon/smithy/build/plugins/BuildInfoPlugin.java @@ -15,8 +15,10 @@ package software.amazon.smithy.build.plugins; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; -import java.util.stream.Collectors; +import java.util.Set; import software.amazon.smithy.build.PluginContext; import software.amazon.smithy.build.SmithyBuildPlugin; import software.amazon.smithy.model.Model; @@ -66,10 +68,7 @@ private static Node serializeBuildInfo(PluginContext context) { info.setProjection(context.getProjection().orElse(null)); info.setValidationEvents(context.getEvents()); info.setTraitNames(findTraitNames(context.getModel())); - info.setTraitDefNames(context.getModel().getShapesWithTrait(TraitDefinition.class).stream() - .map(Shape::getId) - .sorted() - .collect(Collectors.toList())); + info.setTraitDefNames(getTraitShapeIds(context.getModel())); info.setServiceShapeIds(findShapeIds(context.getModel(), ServiceShape.class)); info.setOperationShapeIds(findShapeIds(context.getModel(), OperationShape.class)); info.setResourceShapeIds(findShapeIds(context.getModel(), ResourceShape.class)); @@ -78,18 +77,29 @@ private static Node serializeBuildInfo(PluginContext context) { return mapper.serialize(info); } + private static List getTraitShapeIds(Model model) { + Set traits = model.getShapesWithTrait(TraitDefinition.class); + List result = new ArrayList<>(traits.size()); + for (Shape traitShape : traits) { + result.add(traitShape.getId()); + } + Collections.sort(result); + return result; + } + private static List findTraitNames(Model model) { - return model.shapes() - .flatMap(shape -> shape.getAllTraits().keySet().stream()) - .distinct() - .sorted() - .collect(Collectors.toList()); + List applied = new ArrayList<>(model.getAppliedTraits()); + Collections.sort(applied); + return applied; } private static List findShapeIds(Model model, Class clazz) { - return model.shapes(clazz) - .map(Shape::getId) - .sorted() - .collect(Collectors.toList()); + Set shapes = model.toSet(clazz); + List result = new ArrayList<>(shapes.size()); + for (Shape s : shapes) { + result.add(s.getId()); + } + Collections.sort(result); + return result; } } diff --git a/smithy-build/src/test/java/software/amazon/smithy/build/SmithyBuildTest.java b/smithy-build/src/test/java/software/amazon/smithy/build/SmithyBuildTest.java index d96ff7656b0..f2c83b671b0 100644 --- a/smithy-build/src/test/java/software/amazon/smithy/build/SmithyBuildTest.java +++ b/smithy-build/src/test/java/software/amazon/smithy/build/SmithyBuildTest.java @@ -34,6 +34,7 @@ import java.nio.file.Paths; import java.util.Comparator; import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Optional; @@ -325,12 +326,12 @@ public void appliesPlugins() throws Exception { @Test public void appliesSerialPlugins() throws Exception { - Map plugins = MapUtils.of( - "test1Serial", new Test1SerialPlugin(), - "test2Serial", new Test2SerialPlugin(), - "test1Parallel", new Test1ParallelPlugin(), - "test2Parallel", new Test2ParallelPlugin() - ); + Map plugins = new LinkedHashMap<>(); + plugins.put("test1Serial", new Test1SerialPlugin()); + plugins.put("test2Serial", new Test2SerialPlugin()); + plugins.put("test1Parallel", new Test1ParallelPlugin()); + plugins.put("test2Parallel", new Test2ParallelPlugin()); + Function> factory = SmithyBuildPlugin.createServiceFactory(); Function> composed = name -> OptionalUtils.or( Optional.ofNullable(plugins.get(name)), () -> factory.apply(name)); @@ -352,8 +353,9 @@ public void appliesSerialPlugins() throws Exception { assertPluginPresent("test1Parallel", "hello1Parallel", source, b); assertPluginPresent("test2Parallel", "hello2Parallel", source); - // Both the "a" and "source" projections have serial plugins, so they are run in serial, in alphabetical order. - assertTrue(getPluginFileContents(a, "test1Serial") < getPluginFileContents(source, "test1Serial")); + // "source" contains serial and parallel plugins, so it runs serially and in insetion order. + // This test will need to be changed in the future if we ever optimize how plugins are run in the future. + assertTrue(getPluginFileContents(source, "test1Parallel") < getPluginFileContents(source, "test1Serial")); // The "b" projection has only parallel plugins, so it's a parallel projection. Parallel projections are run // after all the serial projections. assertTrue(getPluginFileContents(source, "test1Serial") < getPluginFileContents(b, "test1Parallel")); diff --git a/smithy-build/src/test/java/software/amazon/smithy/build/Test1ParallelPlugin.java b/smithy-build/src/test/java/software/amazon/smithy/build/Test1ParallelPlugin.java index 202bb8c9b59..05b192d7c1c 100644 --- a/smithy-build/src/test/java/software/amazon/smithy/build/Test1ParallelPlugin.java +++ b/smithy-build/src/test/java/software/amazon/smithy/build/Test1ParallelPlugin.java @@ -13,6 +13,6 @@ public boolean isSerial() { @Override public void execute(PluginContext context) { - context.getFileManifest().writeFile("hello1Parallel", String.format("%s", System.currentTimeMillis())); + context.getFileManifest().writeFile("hello1Parallel", String.format("%s", System.nanoTime())); } } diff --git a/smithy-build/src/test/java/software/amazon/smithy/build/Test1SerialPlugin.java b/smithy-build/src/test/java/software/amazon/smithy/build/Test1SerialPlugin.java index 5d91e6eb967..a3d02b1f8f5 100644 --- a/smithy-build/src/test/java/software/amazon/smithy/build/Test1SerialPlugin.java +++ b/smithy-build/src/test/java/software/amazon/smithy/build/Test1SerialPlugin.java @@ -1,7 +1,5 @@ package software.amazon.smithy.build; -import java.util.concurrent.TimeUnit; - public class Test1SerialPlugin implements SmithyBuildPlugin { @Override public String getName() { @@ -15,12 +13,10 @@ public boolean isSerial() { @Override public void execute(PluginContext context) { - try { - TimeUnit.SECONDS.sleep(1); - context.getFileManifest().writeFile("hello1Serial", String.format("%s", System.currentTimeMillis())); - TimeUnit.SECONDS.sleep(1); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); + int accum = 0; + for (int i = 0; i < 100000; i++) { + accum++; } + context.getFileManifest().writeFile("hello1Serial", String.format("%s", System.nanoTime() + accum)); } } diff --git a/smithy-build/src/test/java/software/amazon/smithy/build/Test2ParallelPlugin.java b/smithy-build/src/test/java/software/amazon/smithy/build/Test2ParallelPlugin.java index bf9f7c59d64..13b5683c406 100644 --- a/smithy-build/src/test/java/software/amazon/smithy/build/Test2ParallelPlugin.java +++ b/smithy-build/src/test/java/software/amazon/smithy/build/Test2ParallelPlugin.java @@ -13,6 +13,6 @@ public boolean isSerial() { @Override public void execute(PluginContext context) { - context.getFileManifest().writeFile("hello2Parallel", String.format("%s", System.currentTimeMillis())); + context.getFileManifest().writeFile("hello2Parallel", String.format("%s", System.nanoTime())); } } diff --git a/smithy-build/src/test/java/software/amazon/smithy/build/Test2SerialPlugin.java b/smithy-build/src/test/java/software/amazon/smithy/build/Test2SerialPlugin.java index 4f49a8eba18..1998af91293 100644 --- a/smithy-build/src/test/java/software/amazon/smithy/build/Test2SerialPlugin.java +++ b/smithy-build/src/test/java/software/amazon/smithy/build/Test2SerialPlugin.java @@ -1,7 +1,5 @@ package software.amazon.smithy.build; -import java.util.concurrent.TimeUnit; - public class Test2SerialPlugin implements SmithyBuildPlugin { @Override public String getName() { @@ -15,12 +13,10 @@ public boolean isSerial() { @Override public void execute(PluginContext context) { - try { - TimeUnit.SECONDS.sleep(1); - context.getFileManifest().writeFile("hello2Serial", String.format("%s", System.currentTimeMillis())); - TimeUnit.SECONDS.sleep(1); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); + int accum = 0; + for (int i = 0; i < 100000; i++) { + accum++; } + context.getFileManifest().writeFile("hello2Serial", String.format("%s", System.currentTimeMillis() + accum)); } } diff --git a/smithy-build/src/test/java/software/amazon/smithy/build/model/SmithyBuildConfigTest.java b/smithy-build/src/test/java/software/amazon/smithy/build/model/SmithyBuildConfigTest.java index 206e499ebc3..1df52ccb4f0 100644 --- a/smithy-build/src/test/java/software/amazon/smithy/build/model/SmithyBuildConfigTest.java +++ b/smithy-build/src/test/java/software/amazon/smithy/build/model/SmithyBuildConfigTest.java @@ -16,6 +16,7 @@ package software.amazon.smithy.build.model; import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.empty; @@ -25,6 +26,7 @@ import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.notNullValue; +import java.io.IOException; import java.net.URISyntaxException; import java.nio.file.Path; import java.nio.file.Paths; @@ -35,6 +37,7 @@ import software.amazon.smithy.build.SmithyBuildException; import software.amazon.smithy.build.SmithyBuildTest; import software.amazon.smithy.model.SourceException; +import software.amazon.smithy.model.SourceLocation; import software.amazon.smithy.model.node.Node; import software.amazon.smithy.model.node.ObjectNode; import software.amazon.smithy.utils.ListUtils; @@ -179,4 +182,32 @@ public void mergingTakesIgnoreMissingPluginsFromEither() { assertThat(a.toBuilder().merge(b).build().isIgnoreMissingPlugins(), equalTo(true)); assertThat(b.toBuilder().merge(a).build().isIgnoreMissingPlugins(), equalTo(true)); } + + @Test + public void loadsFromNode() throws IOException { + Path root = Paths.get("/"); + ObjectNode value = Node.parse(getClass().getResource("loads-from-node.json").openStream()) + .expectObjectNode() + .toBuilder() + .sourceLocation(new SourceLocation( + root.resolve("hello").resolve("smithy-build.json").toString(), 1, 1)) + .build(); + SmithyBuildConfig config = SmithyBuildConfig.fromNode(value); + + assertThat(config.getImports(), contains(root.resolve("hello").resolve("foo.json").toString())); + assertThat(config.getProjections().get("a").getImports(), + contains(root.resolve("hello").resolve("baz.json").toString())); + } + + @Test + public void loadsFromNodeIgnoringBadSourceLocations() throws IOException { + ObjectNode value = Node.parse(getClass().getResource("loads-from-node.json").openStream()) + .expectObjectNode(); + SmithyBuildConfig config = SmithyBuildConfig.fromNode(value); + + Path cwd = Paths.get("."); + + assertThat(config.getImports(), contains(cwd.resolve("foo.json").toString())); + assertThat(config.getProjections().get("a").getImports(), contains(cwd.resolve("baz.json").toString())); + } } diff --git a/smithy-build/src/test/resources/software/amazon/smithy/build/model/loads-from-node.json b/smithy-build/src/test/resources/software/amazon/smithy/build/model/loads-from-node.json new file mode 100644 index 00000000000..523565218db --- /dev/null +++ b/smithy-build/src/test/resources/software/amazon/smithy/build/model/loads-from-node.json @@ -0,0 +1,9 @@ +{ + "version": "1.0", + "imports": ["foo.json"], + "projections": { + "a": { + "imports": ["baz.json"] + } + } +}