Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deprecate KnowledgeIndex reflection #541

Merged
merged 1 commit into from
Aug 26, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ public Map<String, List<String>> updateSecurity(
}

ServiceShape service = context.getService();
AuthorizerIndex authorizerIndex = context.getModel().getKnowledge(AuthorizerIndex.class);
AuthorizerIndex authorizerIndex = AuthorizerIndex.of(context.getModel());

return authorizerIndex.getAuthorizer(service, shape)
// Remove the original scheme authentication scheme from the operation if found.
Expand All @@ -100,7 +100,7 @@ public OperationObject updateOperation(
String path
) {
ServiceShape service = context.getService();
AuthorizerIndex authorizerIndex = context.getModel().getKnowledge(AuthorizerIndex.class);
AuthorizerIndex authorizerIndex = AuthorizerIndex.of(context.getModel());

// Get the resolved security schemes of the service and operation, and
// only add security if it's different than the service.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,8 @@ public OpenApi after(Context<? extends Trait> context, OpenApi openApi) {

private Stream<String> supportedMediaTypes(Context<? extends Trait> context) {
Model model = context.getModel();
HttpBindingIndex httpBindingIndex = context.getModel().getKnowledge(HttpBindingIndex.class);
TopDownIndex topDownIndex = context.getModel().getKnowledge(TopDownIndex.class);
HttpBindingIndex httpBindingIndex = HttpBindingIndex.of(context.getModel());
TopDownIndex topDownIndex = TopDownIndex.of(context.getModel());

// Find the media types defined on all request and response bindings.
return topDownIndex.getContainedOperations(context.getService()).stream()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ public OperationObject updateOperation(
String httpMethod,
String path
) {
IntegrationTraitIndex index = context.getModel().getKnowledge(IntegrationTraitIndex.class);
IntegrationTraitIndex index = IntegrationTraitIndex.of(context.getModel());
return index.getIntegrationTrait(context.getService(), shape)
.map(trait -> operation.toBuilder()
.putExtension(EXTENSION_NAME, createIntegration(context, shape, trait))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,10 @@ public AuthorizerIndex(Model model) {
});
}

public static AuthorizerIndex of(Model model) {
return model.getKnowledge(AuthorizerIndex.class, AuthorizerIndex::new);
}

private static String getNullableAuthorizerValue(Shape shape, String previous) {
return shape.getTrait(AuthorizerTrait.class).map(AuthorizerTrait::getValue).orElse(previous);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public List<ValidationEvent> validate(Model model) {
}

private Optional<ValidationEvent> validateService(Model model, ServiceShape service) {
Set<ShapeId> authSchemes = model.getKnowledge(ServiceIndex.class).getAuthSchemes(service).keySet();
Set<ShapeId> authSchemes = ServiceIndex.of(model).getAuthSchemes(service).keySet();

// Create a comma separated string of authorizer names to schemes.
String invalidMappings = service.getTrait(AuthorizersTrait.class)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
* resource, and service shape in a model.
*/
public final class IntegrationTraitIndex implements KnowledgeIndex {
private Map<ShapeId, Map<ShapeId, Trait>> traits = new HashMap<>();
private final Map<ShapeId, Map<ShapeId, Trait>> traits = new HashMap<>();

public IntegrationTraitIndex(Model model) {
model.shapes(ServiceShape.class).forEach(service -> {
Expand All @@ -43,6 +43,10 @@ public IntegrationTraitIndex(Model model) {
});
}

public static IntegrationTraitIndex of(Model model) {
return model.getKnowledge(IntegrationTraitIndex.class, IntegrationTraitIndex::new);
}

/**
* Get the integration trait for a particular operation, resource, or
* service bound within a service.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public void computesAuthorizers() {
.assemble()
.unwrap();

AuthorizerIndex index = model.getKnowledge(AuthorizerIndex.class);
AuthorizerIndex index = AuthorizerIndex.of(model);
ShapeId serviceA = ShapeId.from("smithy.example#ServiceA");
ShapeId serviceB = ShapeId.from("smithy.example#ServiceB");
ShapeId resourceA = ShapeId.from("smithy.example#ResourceA");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public void resolvesTraits() {
.assemble()
.unwrap();

IntegrationTraitIndex index = model.getKnowledge(IntegrationTraitIndex.class);
IntegrationTraitIndex index = IntegrationTraitIndex.of(model);
ShapeId service = ShapeId.from("ns.foo#Service");
ShapeId a = ShapeId.from("ns.foo#A");
ShapeId b = ShapeId.from("ns.foo#B");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,10 @@ public ConditionKeysIndex(Model model) {
});
}

public static ConditionKeysIndex of(Model model) {
return model.getKnowledge(ConditionKeysIndex.class, ConditionKeysIndex::new);
}

/**
* Get all of the explicit and inferred condition keys used in the entire service.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@
public final class ConditionKeysValidator extends AbstractValidator {
@Override
public List<ValidationEvent> validate(Model model) {
ConditionKeysIndex conditionIndex = model.getKnowledge(ConditionKeysIndex.class);
TopDownIndex topDownIndex = model.getKnowledge(TopDownIndex.class);
ConditionKeysIndex conditionIndex = ConditionKeysIndex.of(model);
TopDownIndex topDownIndex = TopDownIndex.of(model);

return model.shapes(ServiceShape.class)
.filter(service -> service.hasTrait(ServiceTrait.class))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public void successfullyLoadsConditionKeys() {
.unwrap();
ShapeId service = ShapeId.from("smithy.example#MyService");

ConditionKeysIndex index = model.getKnowledge(ConditionKeysIndex.class);
ConditionKeysIndex index = ConditionKeysIndex.of(model);
assertThat(index.getConditionKeyNames(service), containsInAnyOrder(
"aws:accountId", "foo:baz", "myservice:Resource1Id1", "myservice:Resource2Id2"));
assertThat(index.getConditionKeyNames(service, ShapeId.from("smithy.example#Operation1")),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public ArnIndex(Model model) {
.collect(Collectors.toMap(Pair::getLeft, Pair::getRight)));

// Pre-compute all of the ArnTemplates in a service shape.
TopDownIndex topDownIndex = model.getKnowledge(TopDownIndex.class);
TopDownIndex topDownIndex = TopDownIndex.of(model);
List<ServiceShape> services = model.shapes(ServiceShape.class)
.filter(shape -> shape.hasTrait(ServiceTrait.class))
.collect(Collectors.toList());
Expand All @@ -63,12 +63,16 @@ public ArnIndex(Model model) {
.collect(Collectors.toMap(Pair::getLeft, Pair::getRight)));

// Pre-compute all effective ARNs in each service.
IdentifierBindingIndex bindingIndex = model.getKnowledge(IdentifierBindingIndex.class);
IdentifierBindingIndex bindingIndex = IdentifierBindingIndex.of(model);
for (ServiceShape service : services) {
compileEffectiveArns(topDownIndex, bindingIndex, service);
}
}

public static ArnIndex of(Model model) {
return model.getKnowledge(ArnIndex.class, ArnIndex::new);
}

private static String resolveServiceArn(Pair<ServiceShape, ServiceTrait> pair) {
return pair.getRight().getArnNamespace();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public final class ArnTemplateValidator extends AbstractValidator {

@Override
public List<ValidationEvent> validate(Model model) {
ArnIndex arnIndex = model.getKnowledge(ArnIndex.class);
ArnIndex arnIndex = ArnIndex.of(model);
return model.shapes(ServiceShape.class)
.flatMap(service -> Trait.flatMapStream(service, ServiceTrait.class))
.flatMap(pair -> validateService(model, arnIndex, pair.getLeft()))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@ public PlaneIndex(Model model) {
});
}

public static PlaneIndex of(Model model) {
return model.getKnowledge(PlaneIndex.class, PlaneIndex::new);
}

/**
* Checks if the given service shape is part of the control plane.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ private Set<Shape> getOperationsToUpdate(
Model model,
Set<ShapeId> updatedServices
) {
ClientEndpointDiscoveryIndex discoveryIndex = model.getKnowledge(ClientEndpointDiscoveryIndex.class);
ClientEndpointDiscoveryIndex discoveryIndex = ClientEndpointDiscoveryIndex.of(model);
Set<ShapeId> stillBoundOperations = model.shapes(ServiceShape.class)
// Get all endpoint discovery services
.filter(service -> service.hasTrait(ClientEndpointDiscoveryTrait.class))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ public final class ClientEndpointDiscoveryIndex implements KnowledgeIndex {
private final Map<ShapeId, Map<ShapeId, ClientEndpointDiscoveryInfo>> endpointDiscoveryInfo = new HashMap<>();

public ClientEndpointDiscoveryIndex(Model model) {
TopDownIndex topDownIndex = model.getKnowledge(TopDownIndex.class);
OperationIndex opIndex = model.getKnowledge(OperationIndex.class);
TopDownIndex topDownIndex = TopDownIndex.of(model);
OperationIndex opIndex = OperationIndex.of(model);

model.shapes(ServiceShape.class)
.flatMap(service -> Trait.flatMapStream(service, ClientEndpointDiscoveryTrait.class))
Expand All @@ -66,6 +66,10 @@ public ClientEndpointDiscoveryIndex(Model model) {
});
}

public static ClientEndpointDiscoveryIndex of(Model model) {
return model.getKnowledge(ClientEndpointDiscoveryIndex.class, ClientEndpointDiscoveryIndex::new);
}

private Map<ShapeId, ClientEndpointDiscoveryInfo> getOperations(
ServiceShape service,
OperationShape endpointOperation,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ public final class ClientEndpointDiscoveryValidator extends AbstractValidator {

@Override
public List<ValidationEvent> validate(Model model) {
ClientEndpointDiscoveryIndex discoveryIndex = model.getKnowledge(ClientEndpointDiscoveryIndex.class);
OperationIndex opIndex = model.getKnowledge(OperationIndex.class);
ClientEndpointDiscoveryIndex discoveryIndex = ClientEndpointDiscoveryIndex.of(model);
OperationIndex opIndex = OperationIndex.of(model);

Map<ServiceShape, ClientEndpointDiscoveryTrait> endpointDiscoveryServices = model
.shapes(ServiceShape.class)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
public final class ProtocolHttpValidator extends AbstractValidator {
@Override
public List<ValidationEvent> validate(Model model) {
ServiceIndex serviceIndex = model.getKnowledge(ServiceIndex.class);
ServiceIndex serviceIndex = ServiceIndex.of(model);
return model.shapes(ServiceShape.class)
.flatMap(service -> validateService(service, serviceIndex).stream())
.collect(Collectors.toList());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ public void findsEffectiveArns() {
.addImport(ArnIndexTest.class.getResource("effective-arns.json"))
.assemble()
.unwrap();
ArnIndex index = m.getKnowledge(ArnIndex.class);
ArnIndex index = ArnIndex.of(m);
ShapeId service = ShapeId.from("ns.foo#SomeService");

assertThat(index.getEffectiveOperationArn(service, ShapeId.from("ns.foo#InstanceOperation")).map(ArnTrait::getTemplate),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public void computesControlPlaneVsDataPlane() {
.unwrap();

ShapeId service = ShapeId.from("smithy.example#Service");
PlaneIndex index = model.getKnowledge(PlaneIndex.class);
PlaneIndex index = PlaneIndex.of(model);
assertTrue(index.isControlPlane(service));
assertFalse(index.isDataPlane(service));
assertTrue(index.isPlaneDefined(service));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public void getsDiscoveryInformation() {
.addImport(getClass().getResource("test-model.json"))
.assemble()
.unwrap();
ClientEndpointDiscoveryIndex discoveryIndex = result.getKnowledge(ClientEndpointDiscoveryIndex.class);
ClientEndpointDiscoveryIndex discoveryIndex = ClientEndpointDiscoveryIndex.of(result);

ShapeId service = ShapeId.from("ns.foo#FooService");
ShapeId operation = ShapeId.from("ns.foo#GetObject");
Expand All @@ -57,7 +57,7 @@ public void handlesOperationsWithoutConfiguration() {
.addImport(getClass().getResource("test-model.json"))
.assemble()
.unwrap();
ClientEndpointDiscoveryIndex discoveryIndex = result.getKnowledge(ClientEndpointDiscoveryIndex.class);
ClientEndpointDiscoveryIndex discoveryIndex = ClientEndpointDiscoveryIndex.of(result);

ShapeId service = ShapeId.from("ns.foo#FooService");
ShapeId operation = ShapeId.from("ns.foo#DescribeEndpoints");
Expand All @@ -73,7 +73,7 @@ public void handlesServicesWithoutConfiguration() {
.addImport(getClass().getResource("test-model.json"))
.assemble()
.unwrap();
ClientEndpointDiscoveryIndex discoveryIndex = result.getKnowledge(ClientEndpointDiscoveryIndex.class);
ClientEndpointDiscoveryIndex discoveryIndex = ClientEndpointDiscoveryIndex.of(result);

ShapeId service = ShapeId.from("ns.foo#BarService");
ShapeId operation = ShapeId.from("ns.foo#GetObject");
Expand All @@ -89,7 +89,7 @@ public void getsDiscoveryOperations() {
.addImport(getClass().getResource("test-model.json"))
.assemble()
.unwrap();
ClientEndpointDiscoveryIndex discoveryIndex = result.getKnowledge(ClientEndpointDiscoveryIndex.class);
ClientEndpointDiscoveryIndex discoveryIndex = ClientEndpointDiscoveryIndex.of(result);
ShapeId service = ShapeId.from("ns.foo#FooService");
Set<ShapeId> discoveryOperations = discoveryIndex.getEndpointDiscoveryOperations(service);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ private static Optional<String> findMember(Collection<String> haystack, Collecti

@Override
public List<ValidationEvent> validate(Model model) {
OperationIndex operationIndex = model.getKnowledge(OperationIndex.class);
OperationIndex operationIndex = OperationIndex.of(model);
return model.shapes(OperationShape.class)
.filter(shape -> !shape.getTrait(PaginatedTrait.class).isPresent())
.flatMap(shape -> validateShape(model, operationIndex, shape))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ public Set<Shape> evaluateSuboptimalHttpBindingSelector(SelectorState state) {
@Benchmark
public Set<Shape> evaluateHttpBindingManually(SelectorState state) {
Model model = state.model;
TopDownIndex topDownIndex = model.getKnowledge(TopDownIndex.class);
TopDownIndex topDownIndex = TopDownIndex.of(model);
return model.shapes(ServiceShape.class).flatMap(service -> {
Set<OperationShape> operations = topDownIndex.getContainedOperations(service);
// Stop early if there are no bindings at all in the model for any operation.
Expand Down
56 changes: 42 additions & 14 deletions smithy-model/src/main/java/software/amazon/smithy/model/Model.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.stream.Stream;
import software.amazon.smithy.model.knowledge.KnowledgeIndex;
import software.amazon.smithy.model.loader.ModelAssembler;
Expand Down Expand Up @@ -348,24 +349,51 @@ public Builder toBuilder() {
}

/**
* Gets a computed knowledge index of a specific type for the model.
* This method is deprecated. Use the {@code of} method of the
* {@link KnowledgeIndex} you wish to create instead.
*
* <p>If a {@link KnowledgeIndex} of the given type has not yet been
* computed, one will be created using a constructor of the given type
* that accepts a {@link Model}. Computed knowledge indexes are cached
* and returned on subsequent retrievals.
* @param type Type of knowledge index to retrieve.
* @param <T> The type of knowledge index to retrieve.
* @return Returns the computed knowledge index.
*/
@Deprecated
public <T extends KnowledgeIndex> T getKnowledge(Class<T> type) {
return getKnowledge(type, m -> {
try {
return type.getConstructor(Model.class).newInstance(this);
} catch (NoSuchMethodException e) {
String message = String.format(
"KnowledgeIndex for type `%s` does not expose a public constructor that accepts a Model", type);
throw new RuntimeException(message, e);
} catch (ReflectiveOperationException e) {
String message = String.format(
"Unable to create a KnowledgeIndex for type `%s`: %s", type, e.getMessage());
throw new RuntimeException(message, e);
}
});
}

/**
* Gets a computed "knowledge index" of a specific type for the model
* and caches it for subsequent retrieval.
*
* <p>Using this method is preferred over directly instantiating instances
* of a KnowledgeIndex if the KnowledgeIndex required in various unrelated
* code paths where passing around an instance of a KnowledgeIndex is not
* practical or impossible.
* <p>This method should not typically be called directly because
* KnowledgeIndex classes should all provide a public static {@code of}
* method that accepts a {@code Model} and returns an instance of the
* index by invoking {@code getKnowledge}.
*
* <p>If a {@link KnowledgeIndex} of the given type has not yet been
* computed, one will be created using the provided {@code constructor}
* function that accepts a {@link Model}. Computed knowledge indexes are
* cached and returned on subsequent retrievals.
*
* @param type Type of knowledge index to retrieve.
* @param constructor The method used to create {@code type}.
* @param <T> The type of knowledge index to retrieve.
* @return Returns the computed knowledge index.
*/
@SuppressWarnings("unchecked")
public <T extends KnowledgeIndex> T getKnowledge(Class<T> type) {
public <T extends KnowledgeIndex> T getKnowledge(Class<T> type, Function<Model, T> constructor) {
// This method intentionally does not use putIfAbsent to avoid
// deadlocks in the case where a knowledge index needs to access
// other knowledge indexes from a Model. While this *can* cause
Expand All @@ -377,7 +405,7 @@ public <T extends KnowledgeIndex> T getKnowledge(Class<T> type) {
T value = (T) blackboard.get(type);

if (value == null) {
value = KnowledgeIndex.create(type, this);
value = constructor.apply(this);
blackboard.put(type, value);
}

Expand All @@ -388,7 +416,7 @@ public <T extends KnowledgeIndex> T getKnowledge(Class<T> type) {
* Builder used to create a Model.
*/
public static final class Builder implements SmithyBuilder<Model> {
private Map<String, Node> metadata = new HashMap<>();
private final Map<String, Node> metadata = new HashMap<>();
private final Map<ShapeId, Shape> shapeMap = new HashMap<>();

private Builder() {}
Expand Down Expand Up @@ -502,8 +530,8 @@ public Model build() {
}

private static final class TraitCache {
private Map<ShapeId, Set<Shape>> traitIdsToShapes = new HashMap<>();
private Map<Class<? extends Trait>, Set<Shape>> traitsToShapes = new HashMap<>();
private final Map<ShapeId, Set<Shape>> traitIdsToShapes = new HashMap<>();
private final Map<Class<? extends Trait>, Set<Shape>> traitsToShapes = new HashMap<>();

TraitCache(Collection<Shape> shapes) {
for (Shape shape : shapes) {
Expand Down
Loading