Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
io.apicurio.registry.resolver.DefaultSchemaResolver Maven / Gradle / Ivy
/*
* Copyright 2021 Red Hat
*
* Licensed 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 io.apicurio.registry.resolver;
import io.apicurio.registry.resolver.data.Record;
import io.apicurio.registry.resolver.strategy.ArtifactCoordinates;
import io.apicurio.registry.resolver.strategy.ArtifactReference;
import io.apicurio.registry.rest.v2.beans.ArtifactMetaData;
import io.apicurio.registry.rest.v2.beans.IfExists;
import io.apicurio.registry.rest.v2.beans.VersionMetaData;
import io.apicurio.registry.types.ContentTypes;
import io.apicurio.registry.utils.IoUtil;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
/**
* Default implementation of {@link SchemaResolver}
*
* @author Fabian Martinez
* @author Jakub Senko [email protected]
*/
public class DefaultSchemaResolver extends AbstractSchemaResolver {
private boolean autoCreateArtifact;
private IfExists autoCreateBehavior;
private boolean findLatest;
private boolean registerDereferenced;
/**
* @see io.apicurio.registry.resolver.AbstractSchemaResolver#reset()
*/
@Override
public void reset() {
super.reset();
}
/**
* @see io.apicurio.registry.resolver.AbstractSchemaResolver#configure(java.util.Map, io.apicurio.registry.resolver.SchemaParser)
*/
@Override
public void configure(Map configs, SchemaParser schemaParser) {
super.configure(configs, schemaParser);
if (artifactResolverStrategy.loadSchema() && !schemaParser.supportsExtractSchemaFromData()) {
throw new IllegalStateException("Wrong configuration");
}
this.autoCreateArtifact = config.autoRegisterArtifact();
this.registerDereferenced = config.registerDereferenced();
this.autoCreateBehavior = IfExists.fromValue(config.autoRegisterArtifactIfExists());
this.findLatest = config.findLatest();
}
/**
* @see io.apicurio.registry.resolver.SchemaResolver#resolveSchema(io.apicurio.registry.resolver.data.Record)
*/
@Override
public SchemaLookupResult resolveSchema(Record data) {
Objects.requireNonNull(data);
Objects.requireNonNull(data.payload());
ParsedSchema parsedSchema = null;
if (artifactResolverStrategy.loadSchema() && schemaParser.supportsExtractSchemaFromData()) {
parsedSchema = schemaParser.getSchemaFromData(data, registerDereferenced);
}
final ArtifactReference artifactReference = resolveArtifactReference(data, parsedSchema, false, null);
return getSchemaFromCache(artifactReference)
.orElse(getSchemaFromRegistry(parsedSchema, data, artifactReference));
}
private Optional> getSchemaFromCache(ArtifactReference artifactReference) {
if (artifactReference.getGlobalId() != null && schemaCache.containsByGlobalId(artifactReference.getGlobalId())) {
return Optional.of(resolveSchemaByGlobalId(artifactReference.getGlobalId()));
} else if (artifactReference.getContentId() != null && schemaCache.containsByContentId(artifactReference.getContentId())) {
return Optional.of(resolveSchemaByContentId(artifactReference.getContentId()));
} else if (artifactReference.getContentHash() != null && schemaCache.containsByContentHash(artifactReference.getContentHash())) {
return Optional.of(resolveSchemaByContentHash(artifactReference.getContentHash()));
} else if (schemaCache.containsByArtifactCoordinates(ArtifactCoordinates.fromArtifactReference(artifactReference))) {
return Optional.of(resolveSchemaByArtifactCoordinatesCached(ArtifactCoordinates.fromArtifactReference(artifactReference)));
}
return Optional.empty();
}
private SchemaLookupResult getSchemaFromRegistry(ParsedSchema parsedSchema, Record data, ArtifactReference artifactReference) {
if (autoCreateArtifact) {
if (schemaParser.supportsExtractSchemaFromData()) {
if (parsedSchema == null) {
parsedSchema = schemaParser.getSchemaFromData(data, registerDereferenced);
}
if (parsedSchema.hasReferences()) {
//List of references lookup, to be used to create the references for the artifact
final List> schemaLookupResults = handleArtifactReferences(data, parsedSchema);
return handleAutoCreateArtifact(parsedSchema, artifactReference, schemaLookupResults);
} else {
return handleAutoCreateArtifact(parsedSchema, artifactReference);
}
} else if (config.getExplicitSchemaLocation() != null && schemaParser.supportsGetSchemaFromLocation()) {
parsedSchema = schemaParser.getSchemaFromLocation(config.getExplicitSchemaLocation());
return handleAutoCreateArtifact(parsedSchema, artifactReference);
}
}
if (findLatest || artifactReference.getVersion() != null) {
return resolveSchemaByCoordinates(artifactReference.getGroupId(), artifactReference.getArtifactId(), artifactReference.getVersion());
}
if (schemaParser.supportsExtractSchemaFromData()) {
if (parsedSchema == null) {
parsedSchema = schemaParser.getSchemaFromData(data);
}
return handleResolveSchemaByContent(parsedSchema, artifactReference);
}
return resolveSchemaByCoordinates(artifactReference.getGroupId(), artifactReference.getArtifactId(), artifactReference.getVersion());
}
private List> handleArtifactReferences(Record data, ParsedSchema parsedSchema) {
final List> referencesLookup = new ArrayList<>();
for (ParsedSchema referencedSchema : parsedSchema.getSchemaReferences()) {
List> nestedReferences = handleArtifactReferences(data, referencedSchema);
if (nestedReferences.isEmpty()) {
referencesLookup.add(handleAutoCreateArtifact(referencedSchema, resolveArtifactReference(data, referencedSchema, true, referencedSchema.referenceName())));
} else {
referencesLookup.add(handleAutoCreateArtifact(referencedSchema, resolveArtifactReference(data, referencedSchema, true, referencedSchema.referenceName()), nestedReferences));
}
}
return referencesLookup;
}
/**
* @see io.apicurio.registry.resolver.SchemaResolver#resolveSchemaByArtifactReference (io.apicurio.registry.resolver.strategy.ArtifactReferenceImpl)
*/
@Override
public SchemaLookupResult resolveSchemaByArtifactReference(ArtifactReference reference) {
if (reference == null) {
throw new IllegalStateException("artifact reference cannot be null");
}
if (reference.getContentId() != null) {
return resolveSchemaByContentId(reference.getContentId());
}
if (reference.getContentHash() != null) {
return resolveSchemaByContentHash(reference.getContentHash());
}
if (reference.getGlobalId() != null) {
return resolveSchemaByGlobalId(reference.getGlobalId());
}
return resolveSchemaByCoordinates(reference.getGroupId(), reference.getArtifactId(), reference.getVersion());
}
private SchemaLookupResult resolveSchemaByCoordinates(String groupId, String artifactId, String version) {
if (artifactId == null) {
throw new IllegalStateException("artifactId cannot be null");
}
ArtifactReference reference = ArtifactReference.builder().groupId(groupId).artifactId(artifactId).version(version).build();
return resolveSchemaByArtifactReferenceCached(reference);
}
protected SchemaLookupResult resolveSchemaByContentId(long contentId) {
return schemaCache.getByContentId(contentId, contentIdKey -> {
// it's impossible to retrieve more info about the artifact with only the contentId, and that's ok for this case
InputStream rawSchema = client.getContentById(contentIdKey);
//Get the artifact references
final List artifactReferences = client.getArtifactReferencesByContentId(contentId);
//If there are any references for the schema being parsed, resolve them before parsing the schema
final Map> resolvedReferences = resolveReferences(artifactReferences);
byte[] schema = IoUtil.toBytes(rawSchema);
S parsed = schemaParser.parseSchema(schema, resolvedReferences);
SchemaLookupResult.SchemaLookupResultBuilder result = SchemaLookupResult.builder();
ParsedSchemaImpl ps = new ParsedSchemaImpl()
.setParsedSchema(parsed)
.setRawSchema(schema);
return result
.contentId(contentIdKey)
.parsedSchema(ps)
.build();
});
}
protected SchemaLookupResult resolveSchemaByContentHash(String contentHash) {
return schemaCache.getByContentHash(contentHash, contentHashKey -> {
// it's impossible to retrieve more info about the artifact with only the contentHash, and that's ok for this case
InputStream rawSchema = client.getContentByHash(contentHashKey);
//Get the artifact references
final List artifactReferences = client.getArtifactReferencesByContentHash(contentHashKey);
//If there are any references for the schema being parsed, resolve them before parsing the schema
final Map> resolvedReferences = resolveReferences(artifactReferences);
byte[] schema = IoUtil.toBytes(rawSchema);
S parsed = schemaParser.parseSchema(schema, resolvedReferences);
SchemaLookupResult.SchemaLookupResultBuilder result = SchemaLookupResult.builder();
ParsedSchemaImpl ps = new ParsedSchemaImpl()
.setParsedSchema(parsed)
.setRawSchema(schema);
return result
.contentHash(contentHashKey)
.parsedSchema(ps)
.build();
});
}
/**
* Search by content may not work for some usecases of our Serdes implementations.
* For example when serializing protobuf messages, the schema inferred from the data
* may not be equal to the .proto file schema uploaded in the registry.
*/
private SchemaLookupResult handleResolveSchemaByContent(ParsedSchema parsedSchema,
final ArtifactReference artifactReference) {
String rawSchemaString = IoUtil.toString(parsedSchema.getRawSchema());
return schemaCache.getByContent(rawSchemaString, contentKey -> {
VersionMetaData artifactMetadata = client.getArtifactVersionMetaDataByContent(
artifactReference.getGroupId(), artifactReference.getArtifactId(), true, IoUtil.toStream(contentKey));
SchemaLookupResult.SchemaLookupResultBuilder result = SchemaLookupResult.builder();
loadFromArtifactMetaData(artifactMetadata, result);
result.parsedSchema(parsedSchema);
return result.build();
});
}
private SchemaLookupResult handleAutoCreateArtifact(ParsedSchema parsedSchema,
final ArtifactReference artifactReference) {
String rawSchemaString = IoUtil.toString(parsedSchema.getRawSchema());
return schemaCache.getByContent(rawSchemaString, contentKey -> {
ArtifactMetaData artifactMetadata = client.createArtifact(artifactReference.getGroupId(), artifactReference.getArtifactId(), artifactReference.getVersion(),
schemaParser.artifactType(), this.autoCreateBehavior, false, IoUtil.toStream(parsedSchema.getRawSchema()));
SchemaLookupResult.SchemaLookupResultBuilder result = SchemaLookupResult.builder();
loadFromArtifactMetaData(artifactMetadata, result);
result.parsedSchema(parsedSchema);
return result.build();
});
}
private SchemaLookupResult handleAutoCreateArtifact(ParsedSchema parsedSchema,
final ArtifactReference artifactReference, List> referenceLookups) {
String rawSchemaString = IoUtil.toString(parsedSchema.getRawSchema());
final List artifactReferences = parseReferences(referenceLookups);
return schemaCache.getByContent(rawSchemaString, contentKey -> {
ArtifactMetaData artifactMetadata = client.createArtifact(artifactReference.getGroupId(), artifactReference.getArtifactId(), artifactReference.getVersion(),
schemaParser.artifactType(), this.autoCreateBehavior, false, null, null, ContentTypes.APPLICATION_CREATE_EXTENDED, null, null, IoUtil.toStream(parsedSchema.getRawSchema()), artifactReferences);
SchemaLookupResult.SchemaLookupResultBuilder result = SchemaLookupResult.builder();
loadFromArtifactMetaData(artifactMetadata, result);
result.parsedSchema(parsedSchema);
return result.build();
});
}
private List parseReferences(List> referenceLookups) {
final List artifactReferences = new ArrayList<>();
referenceLookups.forEach(referenceLookup -> {
io.apicurio.registry.rest.v2.beans.ArtifactReference artifactReferenceLookup = new io.apicurio.registry.rest.v2.beans.ArtifactReference();
artifactReferenceLookup.setArtifactId(referenceLookup.getArtifactId());
artifactReferenceLookup.setGroupId(referenceLookup.getGroupId());
artifactReferenceLookup.setName(referenceLookup.getParsedSchema().referenceName());
artifactReferenceLookup.setVersion(referenceLookup.getVersion());
artifactReferences.add(artifactReferenceLookup);
});
return artifactReferences;
}
private SchemaLookupResult resolveSchemaByArtifactCoordinatesCached(ArtifactCoordinates artifactCoordinates) {
return schemaCache.getByArtifactCoordinates(artifactCoordinates, artifactCoordinatesKey -> resolveByCoordinates(artifactCoordinatesKey.getGroupId(), artifactCoordinatesKey.getArtifactId(), artifactCoordinatesKey.getVersion()));
}
private SchemaLookupResult resolveSchemaByArtifactReferenceCached(ArtifactReference artifactReference) {
if (artifactReference.getGlobalId() != null) {
return schemaCache.getByGlobalId(artifactReference.getGlobalId(), this::resolveSchemaByGlobalId);
} else if (artifactReference.getContentId() != null) {
return schemaCache.getByContentId(artifactReference.getContentId(), this::resolveSchemaByContentId);
} else if (artifactReference.getContentHash() != null) {
return schemaCache.getByContentHash(artifactReference.getContentHash(), this::resolveSchemaByContentHash);
} else {
return schemaCache.getByArtifactCoordinates(ArtifactCoordinates.fromArtifactReference(artifactReference), artifactReferenceKey -> resolveByCoordinates(artifactReferenceKey.getGroupId(), artifactReferenceKey.getArtifactId(), artifactReferenceKey.getVersion()));
}
}
private SchemaLookupResult resolveByCoordinates(String groupId, String artifactId, String version) {
SchemaLookupResult.SchemaLookupResultBuilder result = SchemaLookupResult.builder();
//TODO if getArtifactVersion returns the artifact version and globalid in the headers we can reduce this to only one http call
Long gid;
if (version == null) {
ArtifactMetaData metadata = client.getArtifactMetaData(groupId, artifactId);
loadFromArtifactMetaData(metadata, result);
gid = metadata.getGlobalId();
} else {
VersionMetaData metadata = client.getArtifactVersionMetaData(
groupId, artifactId, version);
loadFromArtifactMetaData(metadata, result);
gid = metadata.getGlobalId();
}
InputStream rawSchema;
Map> resolvedReferences = new HashMap<>();
if (dereference) {
rawSchema = client.getContentByGlobalId(gid, false, true);
} else {
rawSchema = client.getContentByGlobalId(gid);
//Get the artifact references
final List artifactReferences = client.getArtifactReferencesByGlobalId(gid);
//If there are any references for the schema being parsed, resolve them before parsing the schema
resolvedReferences = resolveReferences(artifactReferences);
}
byte[] schema = IoUtil.toBytes(rawSchema);
S parsed = schemaParser.parseSchema(schema, resolvedReferences);
result.parsedSchema(new ParsedSchemaImpl()
.setParsedSchema(parsed)
.setRawSchema(schema));
return result.build();
}
}