Added checkstyle

This commit is contained in:
Marcin Grzejszczak
2019-02-04 15:55:35 +01:00
parent c6d238085f
commit a8cbf77794
362 changed files with 8885 additions and 6508 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2017 the original author or authors.
* Copyright 2017-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -47,18 +47,19 @@ public class ParsedSchema {
}
public Schema getSchema() {
return schema;
return this.schema;
}
public String getRepresentation() {
return representation;
return this.representation;
}
public SchemaRegistrationResponse getRegistration() {
return registration;
return this.registration;
}
public void setRegistration(SchemaRegistrationResponse registration) {
this.registration = registration;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2016 the original author or authors.
* Copyright 2016-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -24,4 +24,5 @@ public class SchemaNotFoundException extends RuntimeException {
public SchemaNotFoundException(String message) {
super(message);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2016 the original author or authors.
* Copyright 2016-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,6 +20,7 @@ import org.springframework.util.Assert;
/**
* References a schema through its subject and version.
*
* @author Marius Bogoevici
*/
public class SchemaReference {
@@ -97,10 +98,8 @@ public class SchemaReference {
@Override
public String toString() {
return "SchemaReference{" +
"subject='" + this.subject + '\'' +
", version=" + this.version +
", format='" + this.format + '\'' +
'}';
return "SchemaReference{" + "subject='" + this.subject + '\'' + ", version="
+ this.version + ", format='" + this.format + '\'' + '}';
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2016 the original author or authors.
* Copyright 2016-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -40,4 +40,5 @@ public class SchemaRegistrationResponse {
public void setSchemaReference(SchemaReference schemaReference) {
this.schemaReference = schemaReference;
}
}

View File

@@ -47,6 +47,7 @@ import org.springframework.util.MimeType;
/**
* Base class for Apache Avro
* {@link org.springframework.messaging.converter.MessageConverter} implementations.
*
* @author Marius Bogoevici
* @author Vinicius Carvalho
* @author Sercan Karaoglu
@@ -73,11 +74,13 @@ public abstract class AbstractAvroMessageConverter extends AbstractMessageConver
@Override
protected boolean canConvertFrom(Message<?> message, Class<?> targetClass) {
return super.canConvertFrom(message, targetClass) && (message.getPayload() instanceof byte[]);
return super.canConvertFrom(message, targetClass)
&& (message.getPayload() instanceof byte[]);
}
@Override
protected Object convertFromInternal(Message<?> message, Class<?> targetClass, Object conversionHint) {
protected Object convertFromInternal(Message<?> message, Class<?> targetClass,
Object conversionHint) {
Object result = null;
try {
byte[] payload = (byte[]) message.getPayload();
@@ -96,7 +99,8 @@ public abstract class AbstractAvroMessageConverter extends AbstractMessageConver
Schema readerSchema = resolveReaderSchemaForDeserialization(targetClass);
@SuppressWarnings("unchecked")
DatumReader<Object> reader = getDatumReader((Class<Object>) targetClass, readerSchema, writerSchema);
DatumReader<Object> reader = getDatumReader((Class<Object>) targetClass,
readerSchema, writerSchema);
Decoder decoder = DecoderFactory.get().binaryDecoder(payload, null);
result = reader.read(null, decoder);
}
@@ -132,7 +136,8 @@ public abstract class AbstractAvroMessageConverter extends AbstractMessageConver
}
@SuppressWarnings({ "unchecked", "rawtypes" })
protected DatumReader<Object> getDatumReader(Class<Object> type, Schema schema, Schema writerSchema) {
protected DatumReader<Object> getDatumReader(Class<Object> type, Schema schema,
Schema writerSchema) {
DatumReader<Object> reader = null;
if (SpecificRecord.class.isAssignableFrom(type)) {
if (schema != null) {
@@ -167,15 +172,15 @@ public abstract class AbstractAvroMessageConverter extends AbstractMessageConver
}
}
if (reader == null) {
throw new MessageConversionException(
"No schema can be inferred from type " + type
.getName() + " and no schema has been explicitly configured.");
throw new MessageConversionException("No schema can be inferred from type "
+ type.getName() + " and no schema has been explicitly configured.");
}
return reader;
}
@Override
protected Object convertToInternal(Object payload, MessageHeaders headers, Object conversionHint) {
protected Object convertToInternal(Object payload, MessageHeaders headers,
Object conversionHint) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
MimeType hintedContentType = null;
@@ -184,7 +189,8 @@ public abstract class AbstractAvroMessageConverter extends AbstractMessageConver
}
Schema schema = resolveSchemaForWriting(payload, headers, hintedContentType);
@SuppressWarnings("unchecked")
DatumWriter<Object> writer = getDatumWriter((Class<Object>) payload.getClass(), schema);
DatumWriter<Object> writer = getDatumWriter(
(Class<Object>) payload.getClass(), schema);
Encoder encoder = EncoderFactory.get().binaryEncoder(baos, null);
writer.write(payload, encoder);
encoder.flush();
@@ -195,10 +201,11 @@ public abstract class AbstractAvroMessageConverter extends AbstractMessageConver
return baos.toByteArray();
}
protected abstract Schema resolveSchemaForWriting(Object payload, MessageHeaders headers,
MimeType hintedContentType);
protected abstract Schema resolveSchemaForWriting(Object payload,
MessageHeaders headers, MimeType hintedContentType);
protected abstract Schema resolveWriterSchemaForDeserialization(MimeType mimeType);
protected abstract Schema resolveReaderSchemaForDeserialization(Class<?> targetClass);
}

View File

@@ -53,7 +53,8 @@ public class AvroMessageConverterAutoConfiguration {
@StreamMessageConverter
public AvroSchemaRegistryClientMessageConverter avroSchemaMessageConverter(
SchemaRegistryClient schemaRegistryClient) {
AvroSchemaRegistryClientMessageConverter avroSchemaRegistryClientMessageConverter = new AvroSchemaRegistryClientMessageConverter(
AvroSchemaRegistryClientMessageConverter avroSchemaRegistryClientMessageConverter;
avroSchemaRegistryClientMessageConverter = new AvroSchemaRegistryClientMessageConverter(
schemaRegistryClient, cacheManager());
avroSchemaRegistryClientMessageConverter.setDynamicSchemaGenerationEnabled(
this.avroMessageConverterProperties.isDynamicSchemaGenerationEnabled());
@@ -61,28 +62,32 @@ public class AvroMessageConverterAutoConfiguration {
avroSchemaRegistryClientMessageConverter.setReaderSchema(
this.avroMessageConverterProperties.getReaderSchema());
}
if (!ObjectUtils.isEmpty(this.avroMessageConverterProperties.getSchemaLocations())) {
if (!ObjectUtils
.isEmpty(this.avroMessageConverterProperties.getSchemaLocations())) {
avroSchemaRegistryClientMessageConverter.setSchemaLocations(
this.avroMessageConverterProperties.getSchemaLocations());
}
if (!ObjectUtils.isEmpty(this.avroMessageConverterProperties.getSchemaImports())) {
if (!ObjectUtils
.isEmpty(this.avroMessageConverterProperties.getSchemaImports())) {
avroSchemaRegistryClientMessageConverter.setSchemaImports(
this.avroMessageConverterProperties.getSchemaImports());
}
avroSchemaRegistryClientMessageConverter.setPrefix(this.avroMessageConverterProperties.getPrefix());
avroSchemaRegistryClientMessageConverter
.setPrefix(this.avroMessageConverterProperties.getPrefix());
try {
Class<?> clazz = this.avroMessageConverterProperties.getSubjectNamingStrategy();
Class<?> clazz = this.avroMessageConverterProperties
.getSubjectNamingStrategy();
Constructor constructor = ReflectionUtils.accessibleConstructor(clazz);
avroSchemaRegistryClientMessageConverter.setSubjectNamingStrategy(
(SubjectNamingStrategy) constructor.newInstance()
);
} catch (Exception ex) {
throw new IllegalStateException("Unable to create SubjectNamingStrategy " +
this.avroMessageConverterProperties.getSubjectNamingStrategy().toString(),
ex
);
(SubjectNamingStrategy) constructor.newInstance());
}
catch (Exception ex) {
throw new IllegalStateException("Unable to create SubjectNamingStrategy "
+ this.avroMessageConverterProperties.getSubjectNamingStrategy()
.toString(),
ex);
}
return avroSchemaRegistryClientMessageConverter;
@@ -93,4 +98,5 @@ public class AvroMessageConverterAutoConfiguration {
public CacheManager cacheManager() {
return new ConcurrentMapCacheManager();
}
}

View File

@@ -32,17 +32,17 @@ public class AvroMessageConverterProperties {
private Resource readerSchema;
/**
* The source directory of Apache Avro schema. This schema is used by this
* converter. If this schema depends on other schemas consider defining those
* those dependent ones in the {@link #schemaImports}
* The source directory of Apache Avro schema. This schema is used by this converter.
* If this schema depends on other schemas consider defining those those dependent
* ones in the {@link #schemaImports}
* @parameter
*/
private Resource[] schemaLocations;
/**
* A list of files or directories that should be loaded first thus making
* them importable by subsequent schemas. Note that imported files
* should not reference each other.
* A list of files or directories that should be loaded first thus making them
* importable by subsequent schemas. Note that imported files should not reference
* each other.
* @parameter
*/
private Resource[] schemaImports;
@@ -73,7 +73,8 @@ public class AvroMessageConverterProperties {
return this.dynamicSchemaGenerationEnabled;
}
public void setDynamicSchemaGenerationEnabled(boolean dynamicSchemaGenerationEnabled) {
public void setDynamicSchemaGenerationEnabled(
boolean dynamicSchemaGenerationEnabled) {
this.dynamicSchemaGenerationEnabled = dynamicSchemaGenerationEnabled;
}
@@ -86,16 +87,17 @@ public class AvroMessageConverterProperties {
}
public Class<?> getSubjectNamingStrategy() {
return subjectNamingStrategy;
return this.subjectNamingStrategy;
}
public void setSubjectNamingStrategy(Class<? extends SubjectNamingStrategy> subjectNamingStrategy) {
public void setSubjectNamingStrategy(
Class<? extends SubjectNamingStrategy> subjectNamingStrategy) {
Assert.notNull(subjectNamingStrategy, "cannot be null");
this.subjectNamingStrategy = subjectNamingStrategy;
}
public Resource[] getSchemaImports() {
return schemaImports;
return this.schemaImports;
}
public void setSchemaImports(Resource[] schemaImports) {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2016 the original author or authors.
* Copyright 2016-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -32,6 +32,7 @@ import org.springframework.util.MimeType;
* class for {@link org.apache.avro.specific.SpecificRecord} and regular classes, unless a
* specific schema is set, case in which that schema will be used instead. For converting
* to {@link org.apache.avro.generic.GenericRecord} targets, a schema must be set.s
*
* @author Marius Bogoevici
*/
@@ -50,6 +51,8 @@ public class AvroSchemaMessageConverter extends AbstractAvroMessageConverter {
/**
* Create a {@link AvroSchemaMessageConverter}. The converter will be used for the
* provided {@link MimeType}.
* @param supportedMimeType mime type to be supported by
* {@link AvroSchemaMessageConverter}
*/
public AvroSchemaMessageConverter(MimeType supportedMimeType) {
super(supportedMimeType);
@@ -111,4 +114,5 @@ public class AvroSchemaMessageConverter extends AbstractAvroMessageConverter {
MimeType hintedContentType) {
return this.schema;
}
}

View File

@@ -17,7 +17,6 @@
package org.springframework.cloud.stream.schema.avro;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.Map;
@@ -78,27 +77,51 @@ import org.springframework.util.ObjectUtils;
public class AvroSchemaRegistryClientMessageConverter extends AbstractAvroMessageConverter
implements InitializingBean {
/**
* Avro format defined in the Mime type.
*/
public static final String AVRO_FORMAT = "avro";
/**
* Pattern for validating the prefix to be used in the publised subtype.
*/
public static final Pattern PREFIX_VALIDATION_PATTERN = Pattern
.compile("[\\p{Alnum}]");
/**
* Spring Cloud Stream schema property prefix.
*/
public static final String CACHE_PREFIX = "org.springframework.cloud.stream.schema";
/**
* Property for reflection cache.
*/
public static final String REFLECTION_CACHE_NAME = CACHE_PREFIX + ".reflectionCache";
/**
* Property for schema cache.
*/
public static final String SCHEMA_CACHE_NAME = CACHE_PREFIX + ".schemaCache";
/**
* Property for reference cache.
*/
public static final String REFERENCE_CACHE_NAME = CACHE_PREFIX + ".referenceCache";
public static final MimeType DEFAULT_AVRO_MIME_TYPE = new MimeType("application", "*+" + AVRO_FORMAT);
/**
* Default Mime type for Avro.
*/
public static final MimeType DEFAULT_AVRO_MIME_TYPE = new MimeType("application",
"*+" + AVRO_FORMAT);
private final CacheManager cacheManager;
protected Resource[] schemaImports = new Resource[] {};
private Pattern versionedSchema;
private boolean dynamicSchemaGenerationEnabled;
private final CacheManager cacheManager;
private Schema readerSchema;
private Resource[] schemaLocations;
@@ -109,8 +132,6 @@ public class AvroSchemaRegistryClientMessageConverter extends AbstractAvroMessag
private SubjectNamingStrategy subjectNamingStrategy;
protected Resource[] schemaImports = new Resource[]{};
/**
* Creates a new instance, configuring it with {@link SchemaRegistryClient} and
* {@link CacheManager}.
@@ -119,7 +140,8 @@ public class AvroSchemaRegistryClientMessageConverter extends AbstractAvroMessag
* @param cacheManager instance of {@link CacheManager} to cache parsed schemas. If
* caching is not required use {@link NoOpCacheManager}
*/
public AvroSchemaRegistryClientMessageConverter(SchemaRegistryClient schemaRegistryClient, CacheManager cacheManager) {
public AvroSchemaRegistryClientMessageConverter(
SchemaRegistryClient schemaRegistryClient, CacheManager cacheManager) {
super(Collections.singletonList(DEFAULT_AVRO_MIME_TYPE));
Assert.notNull(schemaRegistryClient, "cannot be null");
Assert.notNull(cacheManager, "'cacheManager' cannot be null");
@@ -144,8 +166,7 @@ public class AvroSchemaRegistryClientMessageConverter extends AbstractAvroMessag
/**
* A set of locations where the converter can load schemas from. Schemas provided at
* these locations will be registered automatically.
*
* @param schemaLocations
* @param schemaLocations array of locations
*/
public void setSchemaLocations(Resource[] schemaLocations) {
Assert.notEmpty(schemaLocations, "cannot be empty");
@@ -155,16 +176,15 @@ public class AvroSchemaRegistryClientMessageConverter extends AbstractAvroMessag
/**
* A set of schema locations where should be imported first. Schemas provided at these
* locations will be reference, thus they should not reference each other.
*
* @param schemaImports
* @param schemaImports array of schema imports
*/
public void setSchemaImports(Resource[] schemaImports) {
this.schemaImports = schemaImports;
}
/**
* Set the prefix to be used in the publised subtype. Default 'vnd'.
* @param prefix
* Set the prefix to be used in the published subtype. Default 'vnd'.
* @param prefix prefix to be set
*/
public void setPrefix(String prefix) {
Assert.hasText(prefix, "Prefix cannot be empty");
@@ -190,38 +210,36 @@ public class AvroSchemaRegistryClientMessageConverter extends AbstractAvroMessag
@Override
public void afterPropertiesSet() throws Exception {
this.versionedSchema = Pattern.compile("application/" + this.prefix
+ "\\.([\\p{Alnum}\\$\\.]+)\\.v(\\p{Digit}+)\\+"+AVRO_FORMAT);
+ "\\.([\\p{Alnum}\\$\\.]+)\\.v(\\p{Digit}+)\\+" + AVRO_FORMAT);
Stream.of(this.schemaImports, this.schemaLocations)
.filter(arr -> !ObjectUtils.isEmpty(arr))
.distinct()
.peek(resources -> {
.filter(arr -> !ObjectUtils.isEmpty(arr)).distinct().peek(resources -> {
this.logger.info("Scanning avro schema resources on classpath");
if (this.logger.isInfoEnabled()) {
this.logger.info("Parsing" + this.schemaImports.length);
}
}).flatMap(Arrays::stream).forEach(resource -> {
try {
Schema schema = parseSchema(resource);
if (schema.getType().equals(Schema.Type.UNION)) {
schema.getTypes().forEach(
innerSchema -> registerSchema(resource, innerSchema));
}
else {
registerSchema(resource, schema);
}
}
catch (IOException e) {
if (this.logger.isWarnEnabled()) {
this.logger.warn(
"Failed to parse schema at " + resource.getFilename(),
e);
}
}
});
try {
Schema schema = parseSchema(resource);
if (schema.getType().equals(Schema.Type.UNION)) {
schema.getTypes().forEach(
innerSchema -> registerSchema(resource, innerSchema));
}
else {
registerSchema(resource, schema);
}
}
catch (IOException e) {
if (this.logger.isWarnEnabled()) {
this.logger.warn(
"Failed to parse schema at " + resource.getFilename(),
e);
}
}
});
if (this.cacheManager instanceof NoOpCacheManager) {
logger.warn("Schema caching is effectively disabled "
this.logger.warn("Schema caching is effectively disabled "
+ "since configured cache manager is a NoOpCacheManager. If this was not "
+ "the intention, please provide the appropriate instance of CacheManager "
+ "(i.e., ConcurrentMapCacheManager).");
@@ -229,7 +247,7 @@ public class AvroSchemaRegistryClientMessageConverter extends AbstractAvroMessag
}
protected String toSubject(Schema schema) {
return subjectNamingStrategy.toSubject(schema);
return this.subjectNamingStrategy.toSubject(schema);
}
@Override
@@ -274,10 +292,11 @@ public class AvroSchemaRegistryClientMessageConverter extends AbstractAvroMessag
DirectFieldAccessor dfa = new DirectFieldAccessor(headers);
@SuppressWarnings("unchecked")
Map<String, Object> _headers = (Map<String, Object>) dfa.getPropertyValue("headers");
Map<String, Object> _headers = (Map<String, Object>) dfa
.getPropertyValue("headers");
_headers.put(MessageHeaders.CONTENT_TYPE,
"application/" + this.prefix + "." + schemaReference.getSubject()
+ ".v" + schemaReference.getVersion() + "+" + AVRO_FORMAT);
"application/" + this.prefix + "." + schemaReference.getSubject() + ".v"
+ schemaReference.getVersion() + "+" + AVRO_FORMAT);
return schema;
}
@@ -287,13 +306,17 @@ public class AvroSchemaRegistryClientMessageConverter extends AbstractAvroMessag
if (this.readerSchema == null) {
SchemaReference schemaReference = extractSchemaReference(mimeType);
if (schemaReference != null) {
ParsedSchema parsedSchema = cacheManager.getCache(REFERENCE_CACHE_NAME).get(schemaReference, ParsedSchema.class);
ParsedSchema parsedSchema = this.cacheManager
.getCache(REFERENCE_CACHE_NAME)
.get(schemaReference, ParsedSchema.class);
if (parsedSchema == null) {
String schemaContent = this.schemaRegistryClient.fetch(schemaReference);
String schemaContent = this.schemaRegistryClient
.fetch(schemaReference);
if (schemaContent != null) {
Schema schema = new Schema.Parser().parse(schemaContent);
parsedSchema = new ParsedSchema(schema);
cacheManager.getCache(REFERENCE_CACHE_NAME).putIfAbsent(schemaReference, parsedSchema);
this.cacheManager.getCache(REFERENCE_CACHE_NAME)
.putIfAbsent(schemaReference, parsedSchema);
}
}
if (parsedSchema != null) {
@@ -325,9 +348,10 @@ public class AvroSchemaRegistryClientMessageConverter extends AbstractAvroMessag
.get(payload.getClass().getName(), Schema.class);
if (schema == null) {
if (!isDynamicSchemaGenerationEnabled()) {
throw new SchemaNotFoundException(String
.format("No schema found in the local cache for %s, and dynamic schema generation "
+ "is not enabled", payload.getClass()));
throw new SchemaNotFoundException(String.format(
"No schema found in the local cache for %s, and dynamic schema generation "
+ "is not enabled",
payload.getClass()));
}
else {
schema = ReflectData.get().getSchema(payload.getClass());
@@ -341,15 +365,15 @@ public class AvroSchemaRegistryClientMessageConverter extends AbstractAvroMessag
private void registerSchema(Resource schemaLocation, Schema schema) {
if (this.logger.isInfoEnabled()) {
this.logger.info("Resource " + schemaLocation.getFilename()
+ " parsed into schema " + schema.getNamespace() + "."
+ schema.getName());
this.logger.info(
"Resource " + schemaLocation.getFilename() + " parsed into schema "
+ schema.getNamespace() + "." + schema.getName());
}
this.schemaRegistryClient.register(toSubject(schema), AVRO_FORMAT,
schema.toString());
if (this.logger.isInfoEnabled()) {
this.logger.info("Schema " + schema.getName()
+ " registered with id " + schema);
this.logger
.info("Schema " + schema.getName() + " registered with id " + schema);
}
this.cacheManager.getCache(REFLECTION_CACHE_NAME)
.put(schema.getNamespace() + "." + schema.getName(), schema);
@@ -365,4 +389,5 @@ public class AvroSchemaRegistryClientMessageConverter extends AbstractAvroMessag
}
return schemaReference;
}
}

View File

@@ -27,4 +27,5 @@ public class DefaultSubjectNamingStrategy implements SubjectNamingStrategy {
public String toSubject(Schema schema) {
return schema.getName().toLowerCase();
}
}

View File

@@ -46,13 +46,14 @@ class OriginalContentTypeResolver implements ContentTypeResolver {
mimeType = (MimeType) contentType;
}
else if (contentType instanceof String) {
mimeType = mimeTypeCache.get(contentType);
mimeType = this.mimeTypeCache.get(contentType);
if (mimeType == null) {
String valueAsString = (String) contentType;
mimeType = MimeType.valueOf(valueAsString);
mimeTypeCache.put(valueAsString, mimeType);
this.mimeTypeCache.put(valueAsString, mimeType);
}
}
return mimeType;
}
}

View File

@@ -20,14 +20,17 @@ import org.apache.avro.Schema;
/**
* Provides function towards naming schema registry subjects for Avro files.
*
* @author David Kalosi
*/
public interface SubjectNamingStrategy {
/**
* Takes the Avro schema on input and returns the generated subject under which the schema should be registered.
* @param schema
* Takes the Avro schema on input and returns the generated subject under which the
* schema should be registered.
* @param schema schema to register
* @return subject name
*/
String toSubject(Schema schema);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2017 the original author or authors.
* Copyright 2017-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -45,23 +45,25 @@ public class CachingRegistryClient implements SchemaRegistryClient {
}
@Override
public SchemaRegistrationResponse register(String subject, String format, String schema) {
SchemaRegistrationResponse response = delegate.register(subject, format, schema);
cacheManager.getCache(ID_CACHE).put(response.getId(), schema);
cacheManager.getCache(REF_CACHE).put(response.getSchemaReference(), schema);
public SchemaRegistrationResponse register(String subject, String format,
String schema) {
SchemaRegistrationResponse response = this.delegate.register(subject, format,
schema);
this.cacheManager.getCache(ID_CACHE).put(response.getId(), schema);
this.cacheManager.getCache(REF_CACHE).put(response.getSchemaReference(), schema);
return response;
}
@Override
@Cacheable(cacheNames = REF_CACHE)
public String fetch(SchemaReference schemaReference) {
return delegate.fetch(schemaReference);
return this.delegate.fetch(schemaReference);
}
@Override
@Cacheable(cacheNames = ID_CACHE)
public String fetch(int id) {
return delegate.fetch(id);
return this.delegate.fetch(id);
}
}

View File

@@ -43,7 +43,8 @@ import org.springframework.web.client.RestTemplate;
*/
public class ConfluentSchemaRegistryClient implements SchemaRegistryClient {
private static final List<String> ACCEPT_HEADERS = Arrays.asList("application/vnd.schemaregistry.v1+json",
private static final List<String> ACCEPT_HEADERS = Arrays.asList(
"application/vnd.schemaregistry.v1+json",
"application/vnd.schemaregistry+json", "application/json");
private RestTemplate template;
@@ -95,11 +96,9 @@ public class ConfluentSchemaRegistryClient implements SchemaRegistryClient {
version = getSubjectVersion(subject, payload);
}
catch (HttpStatusCodeException httpException) {
throw new RuntimeException(
String.format(
"Failed to register subject %s, server replied with status %d",
subject, httpException.getStatusCode().value()),
httpException);
throw new RuntimeException(String.format(
"Failed to register subject %s, server replied with status %d",
subject, httpException.getStatusCode().value()), httpException);
}
SchemaRegistrationResponse schemaRegistrationResponse = new SchemaRegistrationResponse();
schemaRegistrationResponse.setId(id);
@@ -112,7 +111,8 @@ public class ConfluentSchemaRegistryClient implements SchemaRegistryClient {
* Confluent register API returns the id, but we need the version of a given schema
* subject. After a successful registration we can inquire the server to get the
* version of a schema
* @param subject
* @param subject the schema subject
* @param payload payload to send
* @return the version of the returned schema
*/
private Integer getSubjectVersion(String subject, String payload) {
@@ -129,11 +129,9 @@ public class ConfluentSchemaRegistryClient implements SchemaRegistryClient {
version = (Integer) response.getBody().get("version");
}
catch (HttpStatusCodeException httpException) {
throw new RuntimeException(
String.format(
"Failed to register subject %s, server replied with status %d",
subject, httpException.getStatusCode().value()),
httpException);
throw new RuntimeException(String.format(
"Failed to register subject %s, server replied with status %d",
subject, httpException.getStatusCode().value()), httpException);
}
return version;
}
@@ -184,4 +182,5 @@ public class ConfluentSchemaRegistryClient implements SchemaRegistryClient {
}
}
}
}

View File

@@ -40,7 +40,7 @@ public class DefaultSchemaRegistryClient implements SchemaRegistryClient {
}
public DefaultSchemaRegistryClient(RestTemplate restTemplate) {
Assert.notNull(restTemplate,"'restTemplate' must not be null.");
Assert.notNull(restTemplate, "'restTemplate' must not be null.");
this.restTemplate = restTemplate;
}
@@ -59,34 +59,37 @@ public class DefaultSchemaRegistryClient implements SchemaRegistryClient {
@SuppressWarnings({ "rawtypes", "unchecked" })
@Override
public SchemaRegistrationResponse register(String subject, String format, String schema) {
public SchemaRegistrationResponse register(String subject, String format,
String schema) {
Map<String, String> requestBody = new HashMap<>();
requestBody.put("subject", subject);
requestBody.put("format", format);
requestBody.put("definition", schema);
ResponseEntity<Map> responseEntity = this.restTemplate.postForEntity(this.endpoint, requestBody, Map.class);
ResponseEntity<Map> responseEntity = this.restTemplate
.postForEntity(this.endpoint, requestBody, Map.class);
if (responseEntity.getStatusCode().is2xxSuccessful()) {
SchemaRegistrationResponse registrationResponse = new SchemaRegistrationResponse();
Map<String, Object> responseBody = (Map<String, Object>) responseEntity.getBody();
Map<String, Object> responseBody = (Map<String, Object>) responseEntity
.getBody();
registrationResponse.setId((Integer) responseBody.get("id"));
registrationResponse.setSchemaReference(
new SchemaReference(subject, (Integer) responseBody.get("version"),
responseBody.get("format").toString()));
return registrationResponse;
}
throw new RuntimeException("Failed to register schema: " + responseEntity.toString());
throw new RuntimeException(
"Failed to register schema: " + responseEntity.toString());
}
@SuppressWarnings("rawtypes")
@Override
public String fetch(SchemaReference schemaReference) {
ResponseEntity<Map> responseEntity = this.restTemplate.getForEntity(
this.endpoint + "/" + schemaReference.getSubject() + "/" + schemaReference
.getFormat() + "/v" + schemaReference
.getVersion(),
Map.class);
ResponseEntity<Map> responseEntity = this.restTemplate.getForEntity(this.endpoint
+ "/" + schemaReference.getSubject() + "/" + schemaReference.getFormat()
+ "/v" + schemaReference.getVersion(), Map.class);
if (!responseEntity.getStatusCode().is2xxSuccessful()) {
throw new RuntimeException("Failed to fetch schema: " + responseEntity.toString());
throw new RuntimeException(
"Failed to fetch schema: " + responseEntity.toString());
}
return (String) responseEntity.getBody().get("definition");
}
@@ -94,11 +97,13 @@ public class DefaultSchemaRegistryClient implements SchemaRegistryClient {
@SuppressWarnings("rawtypes")
@Override
public String fetch(int id) {
ResponseEntity<Map> responseEntity = this.restTemplate.getForEntity(
this.endpoint + "/schemas/" + id, Map.class);
ResponseEntity<Map> responseEntity = this.restTemplate
.getForEntity(this.endpoint + "/schemas/" + id, Map.class);
if (!responseEntity.getStatusCode().is2xxSuccessful()) {
throw new RuntimeException("Failed to fetch schema: " + responseEntity.toString());
throw new RuntimeException(
"Failed to fetch schema: " + responseEntity.toString());
}
return (String) responseEntity.getBody().get("definition");
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2016 the original author or authors.
* Copyright 2016-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2016 the original author or authors.
* Copyright 2016-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -29,6 +29,7 @@ public interface SchemaRegistryClient {
* Registers a schema with the remote repository returning the unique identifier
* associated with this schema.
* @param subject the full name of the schema
* @param format format of the schema
* @param schema string representation of the schema
* @return a {@link SchemaRegistrationResponse} representing the result of the
* operation

View File

@@ -43,12 +43,14 @@ public class SchemaRegistryClientConfiguration {
public SchemaRegistryClient schemaRegistryClient() {
DefaultSchemaRegistryClient defaultSchemaRegistryClient = new DefaultSchemaRegistryClient();
if (StringUtils.hasText(schemaRegistryClientProperties.getEndpoint())) {
defaultSchemaRegistryClient.setEndpoint(schemaRegistryClientProperties.getEndpoint());
if (StringUtils.hasText(this.schemaRegistryClientProperties.getEndpoint())) {
defaultSchemaRegistryClient
.setEndpoint(this.schemaRegistryClientProperties.getEndpoint());
}
SchemaRegistryClient client = (schemaRegistryClientProperties.isCached())
? new CachingRegistryClient(defaultSchemaRegistryClient) : defaultSchemaRegistryClient;
SchemaRegistryClient client = (this.schemaRegistryClientProperties.isCached())
? new CachingRegistryClient(defaultSchemaRegistryClient)
: defaultSchemaRegistryClient;
return client;
}

View File

@@ -38,10 +38,11 @@ public class SchemaRegistryClientProperties {
}
public boolean isCached() {
return cached;
return this.cached;
}
public void setCached(boolean cached) {
this.cached = cached;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2017 the original author or authors.
* Copyright 2017-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -36,7 +36,6 @@ import org.apache.avro.specific.SpecificDatumWriter;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
@@ -57,170 +56,164 @@ import org.springframework.messaging.MessageHeaders;
import org.springframework.util.MimeType;
import org.springframework.util.MimeTypeUtils;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Vinicius Carvalho
* @author Sercan Karaoglu
*/
public class AvroMessageConverterSerializationTests {
Pattern versionedSchema = Pattern.compile("application/" + "vnd"
+ "\\.([\\p{Alnum}\\$\\.]+)\\.v(\\p{Digit}+)\\+avro");
Pattern versionedSchema = Pattern.compile(
"application/" + "vnd" + "\\.([\\p{Alnum}\\$\\.]+)\\.v(\\p{Digit}+)\\+avro");
Log logger = LogFactory.getLog(getClass());
Log logger = LogFactory.getLog(getClass());
private ConfigurableApplicationContext schemaRegistryServerContext;
private ConfigurableApplicationContext schemaRegistryServerContext;
@Before
public void setup() {
schemaRegistryServerContext = SpringApplication
.run(SchemaRegistryServerApplication.class,
"--spring.main.allow-bean-definition-overriding=true");
}
@After
public void tearDown() {
schemaRegistryServerContext.close();
}
@Test
public void testSchemaImport() throws Exception {
SchemaRegistryClient client = new DefaultSchemaRegistryClient();
AvroSchemaRegistryClientMessageConverter converter = new AvroSchemaRegistryClientMessageConverter(
client, new NoOpCacheManager());
converter.setSubjectNamingStrategy(new DefaultSubjectNamingStrategy());
converter.setDynamicSchemaGenerationEnabled(false);
converter.setSchemaLocations(schemaRegistryServerContext
.getResources("classpath:schemas/Command.avsc"));
converter.setSchemaImports(schemaRegistryServerContext
.getResources("classpath:schemas/imports/*.avsc"));
converter.afterPropertiesSet();
Command notification = notification();
Message specificMessage = converter.toMessage(notification,
new MutableMessageHeaders(
Collections.<String, Object> emptyMap()));
Object o = converter.fromMessage(specificMessage, Command.class);
Assert.assertEquals("Serialization issue when use schema-imports", o, notification);
}
@Test
public void sourceWriteSameVersion() throws Exception {
User specificRecord = new User();
specificRecord.setName("joe");
Schema v1 = new Schema.Parser()
.parse(AvroMessageConverterSerializationTests.class
.getClassLoader()
.getResourceAsStream("schemas/user.avsc"));
GenericRecord genericRecord = new GenericData.Record(v1);
genericRecord.put("name", "joe");
SchemaRegistryClient client = new DefaultSchemaRegistryClient();
AvroSchemaRegistryClientMessageConverter converter = new AvroSchemaRegistryClientMessageConverter(
client, new NoOpCacheManager());
converter.setSubjectNamingStrategy(new DefaultSubjectNamingStrategy());
converter.setDynamicSchemaGenerationEnabled(false);
converter.afterPropertiesSet();
Message specificMessage = converter.toMessage(specificRecord,
new MutableMessageHeaders(
Collections.<String, Object> emptyMap()),
MimeTypeUtils.parseMimeType("application/*+avro"));
SchemaReference specificRef = extractSchemaReference(MimeTypeUtils
.parseMimeType(specificMessage.getHeaders().get("contentType")
.toString()));
Message genericMessage = converter.toMessage(genericRecord,
new MutableMessageHeaders(
Collections.<String, Object> emptyMap()),
MimeTypeUtils.parseMimeType("application/*+avro"));
SchemaReference genericRef = extractSchemaReference(MimeTypeUtils
.parseMimeType(genericMessage.getHeaders().get("contentType")
.toString()));
Assert.assertEquals(genericRef, specificRef);
Assert.assertEquals(1, genericRef.getVersion());
}
@Test
public void testOriginalContentTypeHeaderOnly() throws Exception {
User specificRecord = new User();
specificRecord.setName("joe");
Schema v1 = new Schema.Parser()
.parse(AvroMessageConverterSerializationTests.class
.getClassLoader()
.getResourceAsStream("schemas/user.avsc"));
GenericRecord genericRecord = new GenericData.Record(v1);
genericRecord.put("name", "joe");
SchemaRegistryClient client = new DefaultSchemaRegistryClient();
client.register("user", "avro", v1.toString());
AvroSchemaRegistryClientMessageConverter converter = new AvroSchemaRegistryClientMessageConverter(
client, new NoOpCacheManager());
converter.setDynamicSchemaGenerationEnabled(false);
converter.afterPropertiesSet();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DatumWriter<User> writer = new SpecificDatumWriter<>(User.class);
Encoder encoder = EncoderFactory.get().binaryEncoder(baos, null);
writer.write(specificRecord, encoder);
encoder.flush();
Message source = MessageBuilder.withPayload(baos.toByteArray())
.setHeader(MessageHeaders.CONTENT_TYPE,
MimeTypeUtils.APPLICATION_OCTET_STREAM)
.setHeader(BinderHeaders.BINDER_ORIGINAL_CONTENT_TYPE,
"application/vnd.user.v1+avro").build();
Object converted = converter.fromMessage(source, User.class);
Assert.assertNotNull(converted);
Assert.assertEquals(specificRecord.getName().toString(),
((User) converted).getName().toString());
}
private SchemaReference extractSchemaReference(MimeType mimeType) {
SchemaReference schemaReference = null;
Matcher schemaMatcher = this.versionedSchema.matcher(mimeType.toString());
if (schemaMatcher.find()) {
String subject = schemaMatcher.group(1);
Integer version = Integer.parseInt(schemaMatcher.group(2));
schemaReference = new SchemaReference(subject, version,
AvroSchemaRegistryClientMessageConverter.AVRO_FORMAT);
}
return schemaReference;
}
public static Command notification() {
Command messageToSend = getCommandToSend();
messageToSend.setType("notification");
PushNotification pushNotification = new PushNotification();
pushNotification.setArn("google");
pushNotification.setText("hello");
messageToSend.setPayload(pushNotification);
return messageToSend;
}
public static Command sms() {
Command messageToSend = getCommandToSend();
messageToSend.setType("sms");
Sms sms = new Sms();
sms.setPhoneNumber("6141231212");
sms.setText("hello");
messageToSend.setPayload(sms);
return messageToSend;
}
public static Command email() {
Command messageToSend = getCommandToSend();
messageToSend.setType("email");
Email email = new Email();
email.setAddressTo("sercan");
email.setText("hello");
email.setTitle("hi");
messageToSend.setPayload(email);
return messageToSend;
}
public static Command getCommandToSend() {
Command messageToSend = new Command();
messageToSend.setCorrelationId("abc");
return messageToSend;
public static Command notification() {
Command messageToSend = getCommandToSend();
messageToSend.setType("notification");
PushNotification pushNotification = new PushNotification();
pushNotification.setArn("google");
pushNotification.setText("hello");
messageToSend.setPayload(pushNotification);
return messageToSend;
}
public static Command sms() {
Command messageToSend = getCommandToSend();
messageToSend.setType("sms");
Sms sms = new Sms();
sms.setPhoneNumber("6141231212");
sms.setText("hello");
messageToSend.setPayload(sms);
return messageToSend;
}
public static Command email() {
Command messageToSend = getCommandToSend();
messageToSend.setType("email");
Email email = new Email();
email.setAddressTo("sercan");
email.setText("hello");
email.setTitle("hi");
messageToSend.setPayload(email);
return messageToSend;
}
public static Command getCommandToSend() {
Command messageToSend = new Command();
messageToSend.setCorrelationId("abc");
return messageToSend;
}
@Before
public void setup() {
this.schemaRegistryServerContext = SpringApplication.run(
SchemaRegistryServerApplication.class,
"--spring.main.allow-bean-definition-overriding=true");
}
@After
public void tearDown() {
this.schemaRegistryServerContext.close();
}
@Test
public void testSchemaImport() throws Exception {
SchemaRegistryClient client = new DefaultSchemaRegistryClient();
AvroSchemaRegistryClientMessageConverter converter = new AvroSchemaRegistryClientMessageConverter(
client, new NoOpCacheManager());
converter.setSubjectNamingStrategy(new DefaultSubjectNamingStrategy());
converter.setDynamicSchemaGenerationEnabled(false);
converter.setSchemaLocations(this.schemaRegistryServerContext
.getResources("classpath:schemas/Command.avsc"));
converter.setSchemaImports(this.schemaRegistryServerContext
.getResources("classpath:schemas/imports/*.avsc"));
converter.afterPropertiesSet();
Command notification = notification();
Message specificMessage = converter.toMessage(notification,
new MutableMessageHeaders(Collections.<String, Object>emptyMap()));
Object o = converter.fromMessage(specificMessage, Command.class);
assertThat(o).isEqualTo(notification)
.as("Serialization issue when use schema-imports");
}
@Test
public void sourceWriteSameVersion() throws Exception {
User specificRecord = new User();
specificRecord.setName("joe");
Schema v1 = new Schema.Parser().parse(AvroMessageConverterSerializationTests.class
.getClassLoader().getResourceAsStream("schemas/user.avsc"));
GenericRecord genericRecord = new GenericData.Record(v1);
genericRecord.put("name", "joe");
SchemaRegistryClient client = new DefaultSchemaRegistryClient();
AvroSchemaRegistryClientMessageConverter converter = new AvroSchemaRegistryClientMessageConverter(
client, new NoOpCacheManager());
converter.setSubjectNamingStrategy(new DefaultSubjectNamingStrategy());
converter.setDynamicSchemaGenerationEnabled(false);
converter.afterPropertiesSet();
Message specificMessage = converter.toMessage(specificRecord,
new MutableMessageHeaders(Collections.<String, Object>emptyMap()),
MimeTypeUtils.parseMimeType("application/*+avro"));
SchemaReference specificRef = extractSchemaReference(MimeTypeUtils.parseMimeType(
specificMessage.getHeaders().get("contentType").toString()));
Message genericMessage = converter.toMessage(genericRecord,
new MutableMessageHeaders(Collections.<String, Object>emptyMap()),
MimeTypeUtils.parseMimeType("application/*+avro"));
SchemaReference genericRef = extractSchemaReference(MimeTypeUtils.parseMimeType(
genericMessage.getHeaders().get("contentType").toString()));
assertThat(specificRef).isEqualTo(genericRef);
assertThat(genericRef.getVersion()).isEqualTo(1);
}
@Test
public void testOriginalContentTypeHeaderOnly() throws Exception {
User specificRecord = new User();
specificRecord.setName("joe");
Schema v1 = new Schema.Parser().parse(AvroMessageConverterSerializationTests.class
.getClassLoader().getResourceAsStream("schemas/user.avsc"));
GenericRecord genericRecord = new GenericData.Record(v1);
genericRecord.put("name", "joe");
SchemaRegistryClient client = new DefaultSchemaRegistryClient();
client.register("user", "avro", v1.toString());
AvroSchemaRegistryClientMessageConverter converter = new AvroSchemaRegistryClientMessageConverter(
client, new NoOpCacheManager());
converter.setDynamicSchemaGenerationEnabled(false);
converter.afterPropertiesSet();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DatumWriter<User> writer = new SpecificDatumWriter<>(User.class);
Encoder encoder = EncoderFactory.get().binaryEncoder(baos, null);
writer.write(specificRecord, encoder);
encoder.flush();
Message source = MessageBuilder.withPayload(baos.toByteArray())
.setHeader(MessageHeaders.CONTENT_TYPE,
MimeTypeUtils.APPLICATION_OCTET_STREAM)
.setHeader(BinderHeaders.BINDER_ORIGINAL_CONTENT_TYPE,
"application/vnd.user.v1+avro")
.build();
Object converted = converter.fromMessage(source, User.class);
assertThat(converted).isNotNull();
assertThat(specificRecord.getName().toString())
.isEqualTo(((User) converted).getName().toString());
}
private SchemaReference extractSchemaReference(MimeType mimeType) {
SchemaReference schemaReference = null;
Matcher schemaMatcher = this.versionedSchema.matcher(mimeType.toString());
if (schemaMatcher.find()) {
String subject = schemaMatcher.group(1);
Integer version = Integer.parseInt(schemaMatcher.group(2));
schemaReference = new SchemaReference(subject, version,
AvroSchemaRegistryClientMessageConverter.AVRO_FORMAT);
}
return schemaReference;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2016 the original author or authors.
* Copyright 2016-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -54,8 +54,8 @@ public class AvroSchemaMessageConverterTests {
@Test
public void testSendMessageWithLocation() throws Exception {
ConfigurableApplicationContext sourceContext = SpringApplication.run(AvroSourceApplication.class,
"--server.port=0",
ConfigurableApplicationContext sourceContext = SpringApplication.run(
AvroSourceApplication.class, "--server.port=0",
"--spring.jmx.enabled=false",
"--schemaLocation=classpath:schemas/users_v1.schema",
"--spring.cloud.stream.schemaRegistryClient.enabled=false",
@@ -65,12 +65,13 @@ public class AvroSchemaMessageConverterTests {
firstOutboundFoo.setName("foo" + UUID.randomUUID().toString());
firstOutboundFoo.setFavoriteColor("foo" + UUID.randomUUID().toString());
source.output().send(MessageBuilder.withPayload(firstOutboundFoo).build());
MessageCollector sourceMessageCollector = sourceContext.getBean(MessageCollector.class);
Message<?> outboundMessage = sourceMessageCollector.forChannel(source.output()).poll(1000,
TimeUnit.MILLISECONDS);
MessageCollector sourceMessageCollector = sourceContext
.getBean(MessageCollector.class);
Message<?> outboundMessage = sourceMessageCollector.forChannel(source.output())
.poll(1000, TimeUnit.MILLISECONDS);
ConfigurableApplicationContext barSourceContext = SpringApplication.run(AvroSourceApplication.class,
"--server.port=0",
ConfigurableApplicationContext barSourceContext = SpringApplication.run(
AvroSourceApplication.class, "--server.port=0",
"--spring.jmx.enabled=false",
"--schemaLocation=classpath:schemas/users_v1.schema",
"--spring.cloud.stream.schemaRegistryClient.enabled=false",
@@ -81,9 +82,10 @@ public class AvroSchemaMessageConverterTests {
firstOutboundUser2.setFavoritePlace("foo" + UUID.randomUUID().toString());
firstOutboundUser2.setName("foo" + UUID.randomUUID().toString());
barSource.output().send(MessageBuilder.withPayload(firstOutboundUser2).build());
MessageCollector barSourceMessageCollector = barSourceContext.getBean(MessageCollector.class);
Message<?> barOutboundMessage = barSourceMessageCollector.forChannel(barSource.output()).poll(1000,
TimeUnit.MILLISECONDS);
MessageCollector barSourceMessageCollector = barSourceContext
.getBean(MessageCollector.class);
Message<?> barOutboundMessage = barSourceMessageCollector
.forChannel(barSource.output()).poll(1000, TimeUnit.MILLISECONDS);
assertThat(barOutboundMessage).isNotNull();
@@ -92,11 +94,11 @@ public class AvroSchemaMessageConverterTests {
secondUser2OutboundPojo.setFavoritePlace("foo" + UUID.randomUUID().toString());
secondUser2OutboundPojo.setName("foo" + UUID.randomUUID().toString());
source.output().send(MessageBuilder.withPayload(secondUser2OutboundPojo).build());
Message<?> secondBarOutboundMessage = sourceMessageCollector.forChannel(source.output()).poll(1000,
TimeUnit.MILLISECONDS);
Message<?> secondBarOutboundMessage = sourceMessageCollector
.forChannel(source.output()).poll(1000, TimeUnit.MILLISECONDS);
ConfigurableApplicationContext sinkContext = SpringApplication.run(AvroSinkApplication.class,
"--server.port=0",
ConfigurableApplicationContext sinkContext = SpringApplication.run(
AvroSinkApplication.class, "--server.port=0",
"--spring.jmx.enabled=false",
"--spring.cloud.stream.schemaRegistryClient.enabled=false",
"--schemaLocation=classpath:schemas/users_v1.schema");
@@ -104,27 +106,33 @@ public class AvroSchemaMessageConverterTests {
sink.input().send(outboundMessage);
sink.input().send(barOutboundMessage);
sink.input().send(secondBarOutboundMessage);
List<User1> receivedUsers = sinkContext.getBean(AvroSinkApplication.class).receivedUsers;
List<User1> receivedUsers = sinkContext
.getBean(AvroSinkApplication.class).receivedUsers;
assertThat(receivedUsers).hasSize(3);
assertThat(receivedUsers.get(0)).isNotSameAs(firstOutboundFoo);
assertThat(receivedUsers.get(0).getFavoriteColor()).isEqualTo(firstOutboundFoo.getFavoriteColor());
assertThat(receivedUsers.get(0).getFavoriteColor())
.isEqualTo(firstOutboundFoo.getFavoriteColor());
assertThat(receivedUsers.get(0).getName()).isEqualTo(firstOutboundFoo.getName());
assertThat(receivedUsers.get(1)).isNotSameAs(firstOutboundUser2);
assertThat(receivedUsers.get(1).getFavoriteColor()).isEqualTo(firstOutboundUser2.getFavoriteColor());
assertThat(receivedUsers.get(1).getName()).isEqualTo(firstOutboundUser2.getName());
assertThat(receivedUsers.get(1).getFavoriteColor())
.isEqualTo(firstOutboundUser2.getFavoriteColor());
assertThat(receivedUsers.get(1).getName())
.isEqualTo(firstOutboundUser2.getName());
assertThat(receivedUsers.get(2)).isNotSameAs(secondUser2OutboundPojo);
assertThat(receivedUsers.get(2).getFavoriteColor()).isEqualTo(secondUser2OutboundPojo.getFavoriteColor());
assertThat(receivedUsers.get(2).getName()).isEqualTo(secondUser2OutboundPojo.getName());
assertThat(receivedUsers.get(2).getFavoriteColor())
.isEqualTo(secondUser2OutboundPojo.getFavoriteColor());
assertThat(receivedUsers.get(2).getName())
.isEqualTo(secondUser2OutboundPojo.getName());
sourceContext.close();
}
@Test
public void testSendMessageWithoutLocation() throws Exception {
ConfigurableApplicationContext sourceContext = SpringApplication.run(AvroSourceApplication.class,
"--server.port=0",
ConfigurableApplicationContext sourceContext = SpringApplication.run(
AvroSourceApplication.class, "--server.port=0",
"--spring.jmx.enabled=false",
"--spring.cloud.stream.schemaRegistryClient.enabled=false",
"--spring.cloud.stream.bindings.output.contentType=avro/bytes");
@@ -133,12 +141,13 @@ public class AvroSchemaMessageConverterTests {
firstOutboundFoo.setName("foo" + UUID.randomUUID().toString());
firstOutboundFoo.setFavoriteColor("foo" + UUID.randomUUID().toString());
source.output().send(MessageBuilder.withPayload(firstOutboundFoo).build());
MessageCollector sourceMessageCollector = sourceContext.getBean(MessageCollector.class);
Message<?> outboundMessage = sourceMessageCollector.forChannel(source.output()).poll(1000,
TimeUnit.MILLISECONDS);
MessageCollector sourceMessageCollector = sourceContext
.getBean(MessageCollector.class);
Message<?> outboundMessage = sourceMessageCollector.forChannel(source.output())
.poll(1000, TimeUnit.MILLISECONDS);
ConfigurableApplicationContext barSourceContext = SpringApplication.run(AvroSourceApplication.class,
"--server.port=0",
ConfigurableApplicationContext barSourceContext = SpringApplication.run(
AvroSourceApplication.class, "--server.port=0",
"--spring.jmx.enabled=false",
"--spring.cloud.stream.schemaRegistryClient.enabled=false",
"--spring.cloud.stream.bindings.output.contentType=avro/bytes");
@@ -148,9 +157,10 @@ public class AvroSchemaMessageConverterTests {
firstOutboundUser2.setFavoritePlace("foo" + UUID.randomUUID().toString());
firstOutboundUser2.setName("foo" + UUID.randomUUID().toString());
barSource.output().send(MessageBuilder.withPayload(firstOutboundUser2).build());
MessageCollector barSourceMessageCollector = barSourceContext.getBean(MessageCollector.class);
Message<?> barOutboundMessage = barSourceMessageCollector.forChannel(barSource.output()).poll(1000,
TimeUnit.MILLISECONDS);
MessageCollector barSourceMessageCollector = barSourceContext
.getBean(MessageCollector.class);
Message<?> barOutboundMessage = barSourceMessageCollector
.forChannel(barSource.output()).poll(1000, TimeUnit.MILLISECONDS);
assertThat(barOutboundMessage).isNotNull();
@@ -159,30 +169,36 @@ public class AvroSchemaMessageConverterTests {
secondUser2OutboundPojo.setFavoritePlace("foo" + UUID.randomUUID().toString());
secondUser2OutboundPojo.setName("foo" + UUID.randomUUID().toString());
source.output().send(MessageBuilder.withPayload(secondUser2OutboundPojo).build());
Message<?> secondBarOutboundMessage = sourceMessageCollector.forChannel(source.output()).poll(1000,
TimeUnit.MILLISECONDS);
Message<?> secondBarOutboundMessage = sourceMessageCollector
.forChannel(source.output()).poll(1000, TimeUnit.MILLISECONDS);
ConfigurableApplicationContext sinkContext = SpringApplication.run(AvroSinkApplication.class,
"--server.port=0",
ConfigurableApplicationContext sinkContext = SpringApplication.run(
AvroSinkApplication.class, "--server.port=0",
"--spring.jmx.enabled=false",
"--spring.cloud.stream.schemaRegistryClient.enabled=false");
Sink sink = sinkContext.getBean(Sink.class);
sink.input().send(outboundMessage);
sink.input().send(barOutboundMessage);
sink.input().send(secondBarOutboundMessage);
List<User1> receivedUsers = sinkContext.getBean(AvroSinkApplication.class).receivedUsers;
List<User1> receivedUsers = sinkContext
.getBean(AvroSinkApplication.class).receivedUsers;
assertThat(receivedUsers).hasSize(3);
assertThat(receivedUsers.get(0)).isNotSameAs(firstOutboundFoo);
assertThat(receivedUsers.get(0).getFavoriteColor()).isEqualTo(firstOutboundFoo.getFavoriteColor());
assertThat(receivedUsers.get(0).getFavoriteColor())
.isEqualTo(firstOutboundFoo.getFavoriteColor());
assertThat(receivedUsers.get(0).getName()).isEqualTo(firstOutboundFoo.getName());
assertThat(receivedUsers.get(1)).isNotSameAs(firstOutboundUser2);
assertThat(receivedUsers.get(1).getFavoriteColor()).isEqualTo(firstOutboundUser2.getFavoriteColor());
assertThat(receivedUsers.get(1).getName()).isEqualTo(firstOutboundUser2.getName());
assertThat(receivedUsers.get(1).getFavoriteColor())
.isEqualTo(firstOutboundUser2.getFavoriteColor());
assertThat(receivedUsers.get(1).getName())
.isEqualTo(firstOutboundUser2.getName());
assertThat(receivedUsers.get(2)).isNotSameAs(secondUser2OutboundPojo);
assertThat(receivedUsers.get(2).getFavoriteColor()).isEqualTo(secondUser2OutboundPojo.getFavoriteColor());
assertThat(receivedUsers.get(2).getName()).isEqualTo(secondUser2OutboundPojo.getName());
assertThat(receivedUsers.get(2).getFavoriteColor())
.isEqualTo(secondUser2OutboundPojo.getFavoriteColor());
assertThat(receivedUsers.get(2).getName())
.isEqualTo(secondUser2OutboundPojo.getName());
sourceContext.close();
}
@@ -208,11 +224,12 @@ public class AvroSchemaMessageConverterTests {
public MessageConverter userMessageConverter() throws IOException {
AvroSchemaMessageConverter avroSchemaMessageConverter = new AvroSchemaMessageConverter(
MimeType.valueOf("avro/bytes"));
if (schemaLocation != null) {
avroSchemaMessageConverter.setSchemaLocation(schemaLocation);
if (this.schemaLocation != null) {
avroSchemaMessageConverter.setSchemaLocation(this.schemaLocation);
}
return avroSchemaMessageConverter;
}
}
@EnableBinding(Sink.class)
@@ -226,7 +243,7 @@ public class AvroSchemaMessageConverterTests {
@StreamListener(Sink.INPUT)
public void listen(User1 user) {
receivedUsers.add(user);
this.receivedUsers.add(user);
}
public void setSchemaLocation(Resource schemaLocation) {
@@ -238,11 +255,12 @@ public class AvroSchemaMessageConverterTests {
public MessageConverter userMessageConverter() throws IOException {
AvroSchemaMessageConverter avroSchemaMessageConverter = new AvroSchemaMessageConverter(
MimeType.valueOf("avro/bytes"));
if (schemaLocation != null) {
avroSchemaMessageConverter.setSchemaLocation(schemaLocation);
if (this.schemaLocation != null) {
avroSchemaMessageConverter.setSchemaLocation(this.schemaLocation);
}
return avroSchemaMessageConverter;
}
}
}

View File

@@ -59,200 +59,195 @@ import static org.springframework.cloud.schema.avro.AvroMessageConverterSerializ
*/
public class AvroSchemaRegistryClientMessageConverterTests {
static SchemaRegistryClient stubSchemaRegistryClient = new StubSchemaRegistryClient();
static SchemaRegistryClient stubSchemaRegistryClient = new StubSchemaRegistryClient();
private ConfigurableApplicationContext schemaRegistryServerContext;
private ConfigurableApplicationContext schemaRegistryServerContext;
@Before
public void setup() {
schemaRegistryServerContext = SpringApplication
.run(SchemaRegistryServerApplication.class,
"--spring.main.allow-bean-definition-overriding=true");
@Before
public void setup() {
this.schemaRegistryServerContext = SpringApplication.run(
SchemaRegistryServerApplication.class,
"--spring.main.allow-bean-definition-overriding=true");
}
@After
public void tearDown() {
this.schemaRegistryServerContext.close();
}
@Test
public void testSendMessage() throws Exception {
ConfigurableApplicationContext sourceContext = SpringApplication.run(
AvroSourceApplication.class, "--server.port=0",
"--spring.jmx.enabled=false",
"--spring.cloud.stream.bindings.output.contentType=application/*+avro",
"--spring.cloud.stream.schema.avro.dynamicSchemaGenerationEnabled=true");
Source source = sourceContext.getBean(Source.class);
User1 firstOutboundFoo = new User1();
firstOutboundFoo.setFavoriteColor("foo" + UUID.randomUUID().toString());
firstOutboundFoo.setName("foo" + UUID.randomUUID().toString());
source.output().send(MessageBuilder.withPayload(firstOutboundFoo).build());
MessageCollector sourceMessageCollector = sourceContext
.getBean(MessageCollector.class);
Message<?> outboundMessage = sourceMessageCollector.forChannel(source.output())
.poll(1000, TimeUnit.MILLISECONDS);
ConfigurableApplicationContext barSourceContext = SpringApplication.run(
AvroSourceApplication.class, "--server.port=0",
"--spring.jmx.enabled=false",
"--spring.cloud.stream.bindings.output.contentType=application/vnd.user1.v1+avro",
"--spring.cloud.stream.schema.avro.dynamicSchemaGenerationEnabled=true");
Source barSource = barSourceContext.getBean(Source.class);
User2 firstOutboundUser2 = new User2();
firstOutboundUser2.setFavoriteColor("foo" + UUID.randomUUID().toString());
firstOutboundUser2.setName("foo" + UUID.randomUUID().toString());
barSource.output().send(MessageBuilder.withPayload(firstOutboundUser2).build());
MessageCollector barSourceMessageCollector = barSourceContext
.getBean(MessageCollector.class);
Message<?> barOutboundMessage = barSourceMessageCollector
.forChannel(barSource.output()).poll(1000, TimeUnit.MILLISECONDS);
assertThat(barOutboundMessage).isNotNull();
User2 secondBarOutboundPojo = new User2();
secondBarOutboundPojo.setFavoriteColor("foo" + UUID.randomUUID().toString());
secondBarOutboundPojo.setName("foo" + UUID.randomUUID().toString());
source.output().send(MessageBuilder.withPayload(secondBarOutboundPojo).build());
Message<?> secondBarOutboundMessage = sourceMessageCollector
.forChannel(source.output()).poll(1000, TimeUnit.MILLISECONDS);
ConfigurableApplicationContext sinkContext = SpringApplication.run(
AvroSinkApplication.class, "--server.port=0",
"--spring.jmx.enabled=false");
Sink sink = sinkContext.getBean(Sink.class);
sink.input().send(outboundMessage);
sink.input().send(barOutboundMessage);
sink.input().send(secondBarOutboundMessage);
List<User2> receivedPojos = sinkContext
.getBean(AvroSinkApplication.class).receivedPojos;
assertThat(receivedPojos).hasSize(3);
assertThat(receivedPojos.get(0)).isNotSameAs(firstOutboundFoo);
assertThat(receivedPojos.get(0).getFavoriteColor())
.isEqualTo(firstOutboundFoo.getFavoriteColor());
assertThat(receivedPojos.get(0).getName()).isEqualTo(firstOutboundFoo.getName());
assertThat(receivedPojos.get(0).getFavoritePlace()).isEqualTo("NYC");
assertThat(receivedPojos.get(1)).isNotSameAs(firstOutboundUser2);
assertThat(receivedPojos.get(1).getFavoriteColor())
.isEqualTo(firstOutboundUser2.getFavoriteColor());
assertThat(receivedPojos.get(1).getName())
.isEqualTo(firstOutboundUser2.getName());
assertThat(receivedPojos.get(1).getFavoritePlace()).isEqualTo("Boston");
assertThat(receivedPojos.get(2)).isNotSameAs(secondBarOutboundPojo);
assertThat(receivedPojos.get(2).getFavoriteColor())
.isEqualTo(secondBarOutboundPojo.getFavoriteColor());
assertThat(receivedPojos.get(2).getName())
.isEqualTo(secondBarOutboundPojo.getName());
assertThat(receivedPojos.get(2).getFavoritePlace())
.isEqualTo(secondBarOutboundPojo.getFavoritePlace());
sinkContext.close();
barSourceContext.close();
sourceContext.close();
this.schemaRegistryServerContext.close();
}
@Test
public void testSchemaImportConfiguration() throws Exception {
final String[] args = { "--server.port=0", "--spring.jmx.enabled=false",
"--spring.cloud.stream.schema.avro.dynamicSchemaGenerationEnabled=true",
"--spring.cloud.stream.bindings.output.contentType=application/*+avro",
"--spring.cloud.stream.bindings.output.destination=test",
"--spring.cloud.stream.bindings.schema-registry-client.endpoint=http://localhost:8990",
"--spring.cloud.stream.schema.avro.schema-locations=classpath:schemas/Command.avsc",
"--spring.cloud.stream.schema.avro.schema-imports=classpath:schemas/imports/Sms.avsc,"
+ " classpath:schemas/imports/Email.avsc, classpath:schemas/imports/PushNotification.avsc" };
final ConfigurableApplicationContext sourceContext = SpringApplication
.run(AvroSourceApplication.class, args);
final ConfigurableApplicationContext sinkContext = SpringApplication
.run(CommandSinkApplication.class, args);
final Source barSource = sourceContext.getBean(Source.class);
final Command notification = notification();
barSource.output().send(MessageBuilder.withPayload(notification).build());
final MessageCollector barSourceMessageCollector = sourceContext
.getBean(MessageCollector.class);
final Message<?> outboundMessage = barSourceMessageCollector
.forChannel(barSource.output()).poll(1000, TimeUnit.MILLISECONDS);
assertThat(outboundMessage).isNotNull();
Sink sink = sinkContext.getBean(Sink.class);
sink.input().send(outboundMessage);
List<Command> receivedPojos = sinkContext
.getBean(CommandSinkApplication.class).receivedPojos;
assertThat(receivedPojos).hasSize(1);
assertThat(receivedPojos.get(0)).isEqualTo(notification);
}
@Test
public void testNoCacheConfiguration() {
ConfigurableApplicationContext sourceContext = SpringApplication
.run(NoCacheConfiguration.class, "--spring.main.web-environment=false");
AvroSchemaRegistryClientMessageConverter converter = sourceContext
.getBean(AvroSchemaRegistryClientMessageConverter.class);
DirectFieldAccessor accessor = new DirectFieldAccessor(converter);
assertThat(accessor.getPropertyValue("cacheManager"))
.isInstanceOf(NoOpCacheManager.class);
}
@EnableBinding(Source.class)
@EnableAutoConfiguration
@EnableSchemaRegistryClient
public static class AvroSourceApplication {
}
@EnableBinding(Sink.class)
@EnableAutoConfiguration
@EnableSchemaRegistryClient
public static class AvroSinkApplication {
public List<User2> receivedPojos = new ArrayList<>();
@StreamListener(Sink.INPUT)
public void listen(User2 fooPojo) {
this.receivedPojos.add(fooPojo);
}
@After
public void tearDown() {
schemaRegistryServerContext.close();
}
@EnableBinding(Sink.class)
@EnableAutoConfiguration
@EnableSchemaRegistryClient
public static class CommandSinkApplication {
public List<Command> receivedPojos = new ArrayList<>();
@StreamListener(Sink.INPUT)
public void listen(Command fooPojo) {
this.receivedPojos.add(fooPojo);
}
@Test
public void testSendMessage() throws Exception {
}
ConfigurableApplicationContext sourceContext = SpringApplication
.run(AvroSourceApplication.class, "--server.port=0",
"--spring.jmx.enabled=false",
"--spring.cloud.stream.bindings.output.contentType=application/*+avro",
"--spring.cloud.stream.schema.avro.dynamicSchemaGenerationEnabled=true");
Source source = sourceContext.getBean(Source.class);
User1 firstOutboundFoo = new User1();
firstOutboundFoo.setFavoriteColor("foo" + UUID.randomUUID().toString());
firstOutboundFoo.setName("foo" + UUID.randomUUID().toString());
source.output()
.send(MessageBuilder.withPayload(firstOutboundFoo).build());
MessageCollector sourceMessageCollector = sourceContext
.getBean(MessageCollector.class);
Message<?> outboundMessage = sourceMessageCollector
.forChannel(source.output()).poll(1000, TimeUnit.MILLISECONDS);
@Configuration
public static class NoCacheConfiguration {
ConfigurableApplicationContext barSourceContext = SpringApplication
.run(AvroSourceApplication.class, "--server.port=0",
"--spring.jmx.enabled=false",
"--spring.cloud.stream.bindings.output.contentType=application/vnd.user1.v1+avro",
"--spring.cloud.stream.schema.avro.dynamicSchemaGenerationEnabled=true");
Source barSource = barSourceContext.getBean(Source.class);
User2 firstOutboundUser2 = new User2();
firstOutboundUser2.setFavoriteColor("foo" + UUID.randomUUID().toString());
firstOutboundUser2.setName("foo" + UUID.randomUUID().toString());
barSource.output()
.send(MessageBuilder.withPayload(firstOutboundUser2).build());
MessageCollector barSourceMessageCollector = barSourceContext
.getBean(MessageCollector.class);
Message<?> barOutboundMessage = barSourceMessageCollector
.forChannel(barSource.output()).poll(1000, TimeUnit.MILLISECONDS);
assertThat(barOutboundMessage).isNotNull();
User2 secondBarOutboundPojo = new User2();
secondBarOutboundPojo
.setFavoriteColor("foo" + UUID.randomUUID().toString());
secondBarOutboundPojo.setName("foo" + UUID.randomUUID().toString());
source.output()
.send(MessageBuilder.withPayload(secondBarOutboundPojo).build());
Message<?> secondBarOutboundMessage = sourceMessageCollector
.forChannel(source.output()).poll(1000, TimeUnit.MILLISECONDS);
ConfigurableApplicationContext sinkContext = SpringApplication
.run(AvroSinkApplication.class, "--server.port=0",
"--spring.jmx.enabled=false");
Sink sink = sinkContext.getBean(Sink.class);
sink.input().send(outboundMessage);
sink.input().send(barOutboundMessage);
sink.input().send(secondBarOutboundMessage);
List<User2> receivedPojos = sinkContext
.getBean(AvroSinkApplication.class).receivedPojos;
assertThat(receivedPojos).hasSize(3);
assertThat(receivedPojos.get(0)).isNotSameAs(firstOutboundFoo);
assertThat(receivedPojos.get(0).getFavoriteColor())
.isEqualTo(firstOutboundFoo.getFavoriteColor());
assertThat(receivedPojos.get(0).getName())
.isEqualTo(firstOutboundFoo.getName());
assertThat(receivedPojos.get(0).getFavoritePlace()).isEqualTo("NYC");
assertThat(receivedPojos.get(1)).isNotSameAs(firstOutboundUser2);
assertThat(receivedPojos.get(1).getFavoriteColor())
.isEqualTo(firstOutboundUser2.getFavoriteColor());
assertThat(receivedPojos.get(1).getName())
.isEqualTo(firstOutboundUser2.getName());
assertThat(receivedPojos.get(1).getFavoritePlace()).isEqualTo("Boston");
assertThat(receivedPojos.get(2)).isNotSameAs(secondBarOutboundPojo);
assertThat(receivedPojos.get(2).getFavoriteColor())
.isEqualTo(secondBarOutboundPojo.getFavoriteColor());
assertThat(receivedPojos.get(2).getName())
.isEqualTo(secondBarOutboundPojo.getName());
assertThat(receivedPojos.get(2).getFavoritePlace())
.isEqualTo(secondBarOutboundPojo.getFavoritePlace());
sinkContext.close();
barSourceContext.close();
sourceContext.close();
schemaRegistryServerContext.close();
@Bean
@StreamMessageConverter
AvroSchemaRegistryClientMessageConverter avroSchemaRegistryClientMessageConverter() {
return new AvroSchemaRegistryClientMessageConverter(
new DefaultSchemaRegistryClient(), new NoOpCacheManager());
}
@Test
public void testSchemaImportConfiguration() throws Exception{
final String[] args = { "--server.port=0", "--spring.jmx.enabled=false",
"--spring.cloud.stream.schema.avro.dynamicSchemaGenerationEnabled=true",
"--spring.cloud.stream.bindings.output.contentType=application/*+avro",
"--spring.cloud.stream.bindings.output.destination=test",
"--spring.cloud.stream.bindings.schema-registry-client.endpoint=http://localhost:8990",
"--spring.cloud.stream.schema.avro.schema-locations=classpath:schemas/Command.avsc",
"--spring.cloud.stream.schema.avro.schema-imports=classpath:schemas/imports/Sms.avsc, classpath:schemas/imports/Email.avsc, classpath:schemas/imports/PushNotification.avsc" };
final ConfigurableApplicationContext sourceContext = SpringApplication
.run(AvroSourceApplication.class, args);
final ConfigurableApplicationContext sinkContext = SpringApplication
.run(CommandSinkApplication.class, args);
final Source barSource = sourceContext.getBean(Source.class);
final Command notification = notification();
barSource.output()
.send(MessageBuilder.withPayload(notification).build());
final MessageCollector barSourceMessageCollector = sourceContext
.getBean(MessageCollector.class);
final Message<?> outboundMessage = barSourceMessageCollector
.forChannel(barSource.output()).poll(1000, TimeUnit.MILLISECONDS);
assertThat(outboundMessage).isNotNull();
Sink sink = sinkContext.getBean(Sink.class);
sink.input().send(outboundMessage);
List<Command> receivedPojos = sinkContext
.getBean(CommandSinkApplication.class).receivedPojos;
assertThat(receivedPojos).hasSize(1);
assertThat(receivedPojos.get(0)).isEqualTo(notification);
@Bean
ServletWebServerFactory servletWebServerFactory() {
return new TomcatServletWebServerFactory();
}
@Test
public void testNoCacheConfiguration() {
ConfigurableApplicationContext sourceContext = SpringApplication
.run(NoCacheConfiguration.class,
"--spring.main.web-environment=false");
AvroSchemaRegistryClientMessageConverter converter = sourceContext
.getBean(AvroSchemaRegistryClientMessageConverter.class);
DirectFieldAccessor accessor = new DirectFieldAccessor(converter);
assertThat(accessor.getPropertyValue("cacheManager"))
.isInstanceOf(NoOpCacheManager.class);
}
}
@EnableBinding(Source.class)
@EnableAutoConfiguration
@EnableSchemaRegistryClient
public static class AvroSourceApplication {
}
@EnableBinding(Sink.class)
@EnableAutoConfiguration
@EnableSchemaRegistryClient
public static class AvroSinkApplication {
public List<User2> receivedPojos = new ArrayList<>();
@StreamListener(Sink.INPUT)
public void listen(User2 fooPojo) {
receivedPojos.add(fooPojo);
}
}
@EnableBinding(Sink.class)
@EnableAutoConfiguration
@EnableSchemaRegistryClient
public static class CommandSinkApplication {
public List<Command> receivedPojos = new ArrayList<>();
@StreamListener(Sink.INPUT)
public void listen(Command fooPojo) {
receivedPojos.add(fooPojo);
}
}
@Configuration
public static class NoCacheConfiguration {
@Bean
@StreamMessageConverter
AvroSchemaRegistryClientMessageConverter avroSchemaRegistryClientMessageConverter() {
return new AvroSchemaRegistryClientMessageConverter(
new DefaultSchemaRegistryClient(),
new NoOpCacheManager());
}
@Bean
ServletWebServerFactory servletWebServerFactory() {
return new TomcatServletWebServerFactory();
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2016 the original author or authors.
* Copyright 2016-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -47,9 +47,8 @@ public class AvroStubSchemaRegistryClientMessageConverterTests {
@Test
public void testSendMessage() throws Exception {
ConfigurableApplicationContext sourceContext = SpringApplication.run(AvroSourceApplication.class,
"--server.port=0",
"--debug",
ConfigurableApplicationContext sourceContext = SpringApplication.run(
AvroSourceApplication.class, "--server.port=0", "--debug",
"--spring.jmx.enabled=false",
"--spring.cloud.stream.bindings.output.contentType=application/*+avro",
"--spring.cloud.stream.schema.avro.dynamicSchemaGenerationEnabled=true");
@@ -58,12 +57,13 @@ public class AvroStubSchemaRegistryClientMessageConverterTests {
firstOutboundFoo.setFavoriteColor("foo" + UUID.randomUUID().toString());
firstOutboundFoo.setName("foo" + UUID.randomUUID().toString());
source.output().send(MessageBuilder.withPayload(firstOutboundFoo).build());
MessageCollector sourceMessageCollector = sourceContext.getBean(MessageCollector.class);
Message<?> outboundMessage = sourceMessageCollector.forChannel(source.output()).poll(1000,
TimeUnit.MILLISECONDS);
MessageCollector sourceMessageCollector = sourceContext
.getBean(MessageCollector.class);
Message<?> outboundMessage = sourceMessageCollector.forChannel(source.output())
.poll(1000, TimeUnit.MILLISECONDS);
ConfigurableApplicationContext barSourceContext = SpringApplication.run(AvroSourceApplication.class,
"--server.port=0",
ConfigurableApplicationContext barSourceContext = SpringApplication.run(
AvroSourceApplication.class, "--server.port=0",
"--spring.jmx.enabled=false",
"--spring.cloud.stream.bindings.output.contentType=application/vnd.user1.v1+avro",
"--spring.cloud.stream.schema.avro.dynamicSchemaGenerationEnabled=true");
@@ -72,9 +72,10 @@ public class AvroStubSchemaRegistryClientMessageConverterTests {
firstOutboundUser2.setFavoriteColor("foo" + UUID.randomUUID().toString());
firstOutboundUser2.setName("foo" + UUID.randomUUID().toString());
barSource.output().send(MessageBuilder.withPayload(firstOutboundUser2).build());
MessageCollector barSourceMessageCollector = barSourceContext.getBean(MessageCollector.class);
Message<?> barOutboundMessage = barSourceMessageCollector.forChannel(barSource.output()).poll(1000,
TimeUnit.MILLISECONDS);
MessageCollector barSourceMessageCollector = barSourceContext
.getBean(MessageCollector.class);
Message<?> barOutboundMessage = barSourceMessageCollector
.forChannel(barSource.output()).poll(1000, TimeUnit.MILLISECONDS);
assertThat(barOutboundMessage).isNotNull();
@@ -82,31 +83,39 @@ public class AvroStubSchemaRegistryClientMessageConverterTests {
secondBarOutboundPojo.setFavoriteColor("foo" + UUID.randomUUID().toString());
secondBarOutboundPojo.setName("foo" + UUID.randomUUID().toString());
source.output().send(MessageBuilder.withPayload(secondBarOutboundPojo).build());
Message<?> secondBarOutboundMessage = sourceMessageCollector.forChannel(source.output()).poll(1000,
TimeUnit.MILLISECONDS);
Message<?> secondBarOutboundMessage = sourceMessageCollector
.forChannel(source.output()).poll(1000, TimeUnit.MILLISECONDS);
ConfigurableApplicationContext sinkContext = SpringApplication.run(AvroSinkApplication.class,
"--server.port=0", "--spring.jmx.enabled=false");
ConfigurableApplicationContext sinkContext = SpringApplication.run(
AvroSinkApplication.class, "--server.port=0",
"--spring.jmx.enabled=false");
Sink sink = sinkContext.getBean(Sink.class);
sink.input().send(outboundMessage);
sink.input().send(barOutboundMessage);
sink.input().send(secondBarOutboundMessage);
List<User2> receivedPojos = sinkContext.getBean(AvroSinkApplication.class).receivedPojos;
List<User2> receivedPojos = sinkContext
.getBean(AvroSinkApplication.class).receivedPojos;
assertThat(receivedPojos).hasSize(3);
assertThat(receivedPojos.get(0)).isNotSameAs(firstOutboundFoo);
assertThat(receivedPojos.get(0).getFavoriteColor()).isEqualTo(firstOutboundFoo.getFavoriteColor());
assertThat(receivedPojos.get(0).getFavoriteColor())
.isEqualTo(firstOutboundFoo.getFavoriteColor());
assertThat(receivedPojos.get(0).getName()).isEqualTo(firstOutboundFoo.getName());
assertThat(receivedPojos.get(0).getFavoritePlace()).isEqualTo("NYC");
assertThat(receivedPojos.get(1)).isNotSameAs(firstOutboundUser2);
assertThat(receivedPojos.get(1).getFavoriteColor()).isEqualTo(firstOutboundUser2.getFavoriteColor());
assertThat(receivedPojos.get(1).getName()).isEqualTo(firstOutboundUser2.getName());
assertThat(receivedPojos.get(1).getFavoriteColor())
.isEqualTo(firstOutboundUser2.getFavoriteColor());
assertThat(receivedPojos.get(1).getName())
.isEqualTo(firstOutboundUser2.getName());
assertThat(receivedPojos.get(1).getFavoritePlace()).isEqualTo("Boston");
assertThat(receivedPojos.get(2)).isNotSameAs(secondBarOutboundPojo);
assertThat(receivedPojos.get(2).getFavoriteColor()).isEqualTo(secondBarOutboundPojo.getFavoriteColor());
assertThat(receivedPojos.get(2).getName()).isEqualTo(secondBarOutboundPojo.getName());
assertThat(receivedPojos.get(2).getFavoritePlace()).isEqualTo(secondBarOutboundPojo.getFavoritePlace());
assertThat(receivedPojos.get(2).getFavoriteColor())
.isEqualTo(secondBarOutboundPojo.getFavoriteColor());
assertThat(receivedPojos.get(2).getName())
.isEqualTo(secondBarOutboundPojo.getName());
assertThat(receivedPojos.get(2).getFavoritePlace())
.isEqualTo(secondBarOutboundPojo.getFavoritePlace());
sourceContext.close();
}
@@ -119,6 +128,7 @@ public class AvroStubSchemaRegistryClientMessageConverterTests {
public SchemaRegistryClient schemaRegistryClient() {
return stubSchemaRegistryClient;
}
}
@EnableBinding(Sink.class)
@@ -129,7 +139,7 @@ public class AvroStubSchemaRegistryClientMessageConverterTests {
@StreamListener(Sink.INPUT)
public void listen(User2 fooPojo) {
receivedPojos.add(fooPojo);
this.receivedPojos.add(fooPojo);
}
@Bean
@@ -138,4 +148,5 @@ public class AvroStubSchemaRegistryClientMessageConverterTests {
}
}
}

View File

@@ -32,4 +32,5 @@ class CustomSubjectNamingStrategy implements SubjectNamingStrategy {
public String toSubject(Schema schema) {
return schema.getFullName();
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2016 the original author or authors.
* Copyright 2016-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -39,12 +39,14 @@ public class StubSchemaRegistryClient implements SchemaRegistryClient {
private final Map<String, Map<Integer, SchemaWithId>> storedSchemas = new HashMap<>();
@Override
public SchemaRegistrationResponse register(String subject, String format, String schema) {
public SchemaRegistrationResponse register(String subject, String format,
String schema) {
if (!this.storedSchemas.containsKey(subject)) {
this.storedSchemas.put(subject, new TreeMap<Integer, SchemaWithId>());
}
Map<Integer, SchemaWithId> schemaVersions = this.storedSchemas.get(subject);
for (Map.Entry<Integer, SchemaWithId> integerSchemaEntry : schemaVersions.entrySet()) {
for (Map.Entry<Integer, SchemaWithId> integerSchemaEntry : schemaVersions
.entrySet()) {
if (integerSchemaEntry.getValue().getSchema().equals(schema)) {
SchemaRegistrationResponse schemaRegistrationResponse = new SchemaRegistrationResponse();
@@ -60,24 +62,27 @@ public class StubSchemaRegistryClient implements SchemaRegistryClient {
schemaVersions.put(nextVersion, new SchemaWithId(id, schema));
SchemaRegistrationResponse schemaRegistrationResponse = new SchemaRegistrationResponse();
schemaRegistrationResponse.setId(this.index.getAndIncrement());
schemaRegistrationResponse.setSchemaReference(
new SchemaReference(subject, nextVersion, AvroSchemaRegistryClientMessageConverter.AVRO_FORMAT));
schemaRegistrationResponse.setSchemaReference(new SchemaReference(subject,
nextVersion, AvroSchemaRegistryClientMessageConverter.AVRO_FORMAT));
this.schemasById.put(id, schema);
return schemaRegistrationResponse;
}
@Override
public String fetch(SchemaReference schemaReference) {
if (!AvroSchemaRegistryClientMessageConverter.AVRO_FORMAT.equals(schemaReference.getFormat())) {
if (!AvroSchemaRegistryClientMessageConverter.AVRO_FORMAT
.equals(schemaReference.getFormat())) {
throw new IllegalArgumentException("Only 'avro' is supported by this client");
}
if (!this.storedSchemas.containsKey(schemaReference.getSubject())) {
throw new SchemaNotFoundException("Not found: " + schemaReference);
}
if (!this.storedSchemas.get(schemaReference.getSubject()).containsKey(schemaReference.getVersion())) {
if (!this.storedSchemas.get(schemaReference.getSubject())
.containsKey(schemaReference.getVersion())) {
throw new SchemaNotFoundException("Not found: " + schemaReference);
}
return this.storedSchemas.get(schemaReference.getSubject()).get(schemaReference.getVersion()).getSchema();
return this.storedSchemas.get(schemaReference.getSubject())
.get(schemaReference.getVersion()).getSchema();
}
@Override
@@ -103,6 +108,7 @@ public class StubSchemaRegistryClient implements SchemaRegistryClient {
public String getSchema() {
return this.schema;
}
}
}

View File

@@ -44,13 +44,13 @@ public class SubjectNamingStrategyTest {
@Test
public void testCustomNamingStrategy() throws Exception {
ConfigurableApplicationContext sourceContext = SpringApplication.run(AvroSourceApplication.class,
"--server.port=0",
"--debug",
"--spring.jmx.enabled=false",
"--spring.cloud.stream.bindings.output.contentType=application/*+avro",
"--spring.cloud.stream.schema.avro.subjectNamingStrategy=org.springframework.cloud.schema.avro.CustomSubjectNamingStrategy",
"--spring.cloud.stream.schema.avro.dynamicSchemaGenerationEnabled=true");
ConfigurableApplicationContext sourceContext = SpringApplication.run(
AvroSourceApplication.class, "--server.port=0", "--debug",
"--spring.jmx.enabled=false",
"--spring.cloud.stream.bindings.output.contentType=application/*+avro",
"--spring.cloud.stream.schema.avro.subjectNamingStrategy="
+ "org.springframework.cloud.schema.avro.CustomSubjectNamingStrategy",
"--spring.cloud.stream.schema.avro.dynamicSchemaGenerationEnabled=true");
Source source = sourceContext.getBean(Source.class);
User1 user1 = new User1();
@@ -58,11 +58,13 @@ public class SubjectNamingStrategyTest {
user1.setName("foo" + UUID.randomUUID().toString());
source.output().send(MessageBuilder.withPayload(user1).build());
MessageCollector barSourceMessageCollector = sourceContext.getBean(MessageCollector.class);
Message<?> message = barSourceMessageCollector.forChannel(source.output()).poll(1000, TimeUnit.MILLISECONDS);
MessageCollector barSourceMessageCollector = sourceContext
.getBean(MessageCollector.class);
Message<?> message = barSourceMessageCollector.forChannel(source.output())
.poll(1000, TimeUnit.MILLISECONDS);
assertThat(message.getHeaders().get("contentType"))
.isEqualTo(MimeType.valueOf("application/vnd.org.springframework.cloud.schema.avro.User1.v1+avro"));
assertThat(message.getHeaders().get("contentType")).isEqualTo(MimeType.valueOf(
"application/vnd.org.springframework.cloud.schema.avro.User1.v1+avro"));
}
@EnableBinding(Source.class)
@@ -73,5 +75,7 @@ public class SubjectNamingStrategyTest {
public SchemaRegistryClient schemaRegistryClient() {
return stubSchemaRegistryClient;
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2016 the original author or authors.
* Copyright 2016-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -54,4 +54,5 @@ public class User1 {
public void setFavoriteColor(String favoriteColor) {
this.favoriteColor = favoriteColor;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2016 the original author or authors.
* Copyright 2016-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -66,4 +66,5 @@ public class User2 {
public void setFavoritePlace(String favoritePlace) {
this.favoritePlace = favoritePlace;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2017 the original author or authors.
* Copyright 2017-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,7 +16,6 @@
package org.springframework.cloud.schema.avro.client;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
@@ -31,6 +30,7 @@ import org.springframework.test.web.client.MockRestServiceServer;
import org.springframework.web.client.HttpStatusCodeException;
import org.springframework.web.client.RestTemplate;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.test.web.client.match.MockRestRequestMatchers.header;
import static org.springframework.test.web.client.match.MockRestRequestMatchers.method;
import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo;
@@ -38,122 +38,139 @@ import static org.springframework.test.web.client.response.MockRestResponseCreat
import static org.springframework.test.web.client.response.MockRestResponseCreators.withStatus;
import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess;
/**
* @author Vinicius Carvalho
*/
public class ConfluentSchemaRegistryClientTests {
private RestTemplate restTemplate;
private MockRestServiceServer mockRestServiceServer;
@Before
public void setup(){
public void setup() {
this.restTemplate = new RestTemplate();
this.mockRestServiceServer = MockRestServiceServer.createServer(restTemplate);
this.mockRestServiceServer = MockRestServiceServer
.createServer(this.restTemplate);
}
@Test
public void registerSchema() throws Exception{
this.mockRestServiceServer.expect(requestTo("http://localhost:8081/subjects/user/versions"))
public void registerSchema() throws Exception {
this.mockRestServiceServer
.expect(requestTo("http://localhost:8081/subjects/user/versions"))
.andExpect(method(HttpMethod.POST))
.andExpect(header("Content-Type","application/json"))
.andExpect(header("Accept","application/vnd.schemaregistry.v1+json"))
.andExpect(header("Content-Type", "application/json"))
.andExpect(header("Accept", "application/vnd.schemaregistry.v1+json"))
.andRespond(withSuccess("{\"id\":101}", MediaType.APPLICATION_JSON));
this.mockRestServiceServer.expect(requestTo("http://localhost:8081/subjects/user"))
this.mockRestServiceServer
.expect(requestTo("http://localhost:8081/subjects/user"))
.andExpect(method(HttpMethod.POST))
.andExpect(header("Content-Type","application/json"))
.andExpect(header("Accept","application/vnd.schemaregistry.v1+json"))
.andExpect(header("Content-Type", "application/json"))
.andExpect(header("Accept", "application/vnd.schemaregistry.v1+json"))
.andRespond(withSuccess("{\"version\":1}", MediaType.APPLICATION_JSON));
ConfluentSchemaRegistryClient client = new ConfluentSchemaRegistryClient(this.restTemplate);
SchemaRegistrationResponse response = client.register("user","avro","{}");
Assert.assertEquals(1,response.getSchemaReference().getVersion());
Assert.assertEquals(101,response.getId());
ConfluentSchemaRegistryClient client = new ConfluentSchemaRegistryClient(
this.restTemplate);
SchemaRegistrationResponse response = client.register("user", "avro", "{}");
assertThat(response.getSchemaReference().getVersion()).isEqualTo(1);
assertThat(response.getId()).isEqualTo(101);
this.mockRestServiceServer.verify();
}
@Test(expected = RuntimeException.class)
public void registerWithInvalidJson() {
this.mockRestServiceServer.expect(requestTo("http://localhost:8081/subjects/user/versions"))
this.mockRestServiceServer
.expect(requestTo("http://localhost:8081/subjects/user/versions"))
.andExpect(method(HttpMethod.POST))
.andExpect(header("Content-Type","application/json"))
.andExpect(header("Accept","application/vnd.schemaregistry.v1+json"))
.andExpect(header("Content-Type", "application/json"))
.andExpect(header("Accept", "application/vnd.schemaregistry.v1+json"))
.andRespond(withBadRequest());
ConfluentSchemaRegistryClient client = new ConfluentSchemaRegistryClient(this.restTemplate);
SchemaRegistrationResponse response = client.register("user","avro","<>");
ConfluentSchemaRegistryClient client = new ConfluentSchemaRegistryClient(
this.restTemplate);
SchemaRegistrationResponse response = client.register("user", "avro", "<>");
}
@Test
public void registerIncompatibleSchema() {
this.mockRestServiceServer.expect(requestTo("http://localhost:8081/subjects/user/versions"))
this.mockRestServiceServer
.expect(requestTo("http://localhost:8081/subjects/user/versions"))
.andExpect(method(HttpMethod.POST))
.andExpect(header("Content-Type","application/json"))
.andExpect(header("Accept","application/vnd.schemaregistry.v1+json"))
.andExpect(header("Content-Type", "application/json"))
.andExpect(header("Accept", "application/vnd.schemaregistry.v1+json"))
.andRespond(withStatus(HttpStatus.CONFLICT));
ConfluentSchemaRegistryClient client = new ConfluentSchemaRegistryClient(this.restTemplate);
ConfluentSchemaRegistryClient client = new ConfluentSchemaRegistryClient(
this.restTemplate);
Exception expected = null;
try {
SchemaRegistrationResponse response = client.register("user","avro","{}");
SchemaRegistrationResponse response = client.register("user", "avro", "{}");
}
catch (Exception e) {
expected = e;
}
Assert.assertTrue(expected instanceof RuntimeException);
Assert.assertTrue(expected.getCause() instanceof HttpStatusCodeException);
assertThat(expected instanceof RuntimeException).isTrue();
assertThat(expected.getCause() instanceof HttpStatusCodeException).isTrue();
this.mockRestServiceServer.verify();
}
@Test
public void responseErrorFetch() {
this.mockRestServiceServer.expect(requestTo("http://localhost:8081/subjects/user/versions"))
this.mockRestServiceServer
.expect(requestTo("http://localhost:8081/subjects/user/versions"))
.andExpect(method(HttpMethod.POST))
.andExpect(header("Content-Type","application/json"))
.andExpect(header("Accept","application/vnd.schemaregistry.v1+json"))
.andExpect(header("Content-Type", "application/json"))
.andExpect(header("Accept", "application/vnd.schemaregistry.v1+json"))
.andRespond(withSuccess("{\"id\":101}", MediaType.APPLICATION_JSON));
this.mockRestServiceServer.expect(requestTo("http://localhost:8081/subjects/user"))
this.mockRestServiceServer
.expect(requestTo("http://localhost:8081/subjects/user"))
.andExpect(method(HttpMethod.POST))
.andExpect(header("Content-Type","application/json"))
.andExpect(header("Accept","application/vnd.schemaregistry.v1+json"))
.andExpect(header("Content-Type", "application/json"))
.andExpect(header("Accept", "application/vnd.schemaregistry.v1+json"))
.andRespond(withBadRequest());
ConfluentSchemaRegistryClient client = new ConfluentSchemaRegistryClient(this.restTemplate);
ConfluentSchemaRegistryClient client = new ConfluentSchemaRegistryClient(
this.restTemplate);
Exception expected = null;
try {
SchemaRegistrationResponse response = client.register("user","avro","{}");
SchemaRegistrationResponse response = client.register("user", "avro", "{}");
}
catch (Exception e) {
expected = e;
}
Assert.assertTrue(expected instanceof RuntimeException);
Assert.assertTrue(expected.getCause() instanceof HttpStatusCodeException);
assertThat(expected instanceof RuntimeException).isTrue();
assertThat(expected.getCause() instanceof HttpStatusCodeException).isTrue();
this.mockRestServiceServer.verify();
}
@Test
public void findByReference(){
this.mockRestServiceServer.expect(requestTo("http://localhost:8081/subjects/user/versions/1"))
public void findByReference() {
this.mockRestServiceServer
.expect(requestTo("http://localhost:8081/subjects/user/versions/1"))
.andExpect(method(HttpMethod.GET))
.andExpect(header("Content-Type","application/vnd.schemaregistry.v1+json"))
.andExpect(header("Accept","application/vnd.schemaregistry.v1+json"))
.andExpect(
header("Content-Type", "application/vnd.schemaregistry.v1+json"))
.andExpect(header("Accept", "application/vnd.schemaregistry.v1+json"))
.andRespond(withSuccess("{\"schema\":\"\"}", MediaType.APPLICATION_JSON));
ConfluentSchemaRegistryClient client = new ConfluentSchemaRegistryClient(this.restTemplate);
SchemaReference reference = new SchemaReference("user",1,"avro");
ConfluentSchemaRegistryClient client = new ConfluentSchemaRegistryClient(
this.restTemplate);
SchemaReference reference = new SchemaReference("user", 1, "avro");
String schema = client.fetch(reference);
Assert.assertEquals("",schema);
assertThat(schema).isEqualTo("");
this.mockRestServiceServer.verify();
}
@Test(expected = SchemaNotFoundException.class)
public void schemaNotFound(){
this.mockRestServiceServer.expect(requestTo("http://localhost:8081/subjects/user/versions/1"))
public void schemaNotFound() {
this.mockRestServiceServer
.expect(requestTo("http://localhost:8081/subjects/user/versions/1"))
.andExpect(method(HttpMethod.GET))
.andExpect(header("Content-Type","application/vnd.schemaregistry.v1+json"))
.andExpect(header("Accept","application/vnd.schemaregistry.v1+json"))
.andExpect(
header("Content-Type", "application/vnd.schemaregistry.v1+json"))
.andExpect(header("Accept", "application/vnd.schemaregistry.v1+json"))
.andRespond(withStatus(HttpStatus.NOT_FOUND));
ConfluentSchemaRegistryClient client = new ConfluentSchemaRegistryClient(this.restTemplate);
SchemaReference reference = new SchemaReference("user",1,"avro");
ConfluentSchemaRegistryClient client = new ConfluentSchemaRegistryClient(
this.restTemplate);
SchemaReference reference = new SchemaReference("user", 1, "avro");
String schema = client.fetch(reference);
}

View File

@@ -16,4 +16,4 @@
"type":["Sms", "Email", "PushNotification"]
}
]
}
}

View File

@@ -16,4 +16,4 @@
"type":"string"
}
]
}
}

View File

@@ -12,4 +12,4 @@
"type":"string"
}
]
}
}

View File

@@ -11,4 +11,4 @@
"type":"string"
}
]
}
}

View File

@@ -7,4 +7,4 @@
{"name": "text", "type": "string"},
{"name": "timestamp", "type": "long"}
]
}
}

View File

@@ -7,4 +7,4 @@
{"name": "favoriteColor", "type": ["string", "null"]}
]
}
}

View File

@@ -7,4 +7,4 @@
{"name": "favoriteColor", "type": ["string", "null"]},
{"name": "favoritePlace", "type": ["string","null"], "default" : "NYC"}
]
}
}