diff --git a/lang/java/avro/pom.xml b/lang/java/avro/pom.xml
index e0484db050c..233208a87cb 100644
--- a/lang/java/avro/pom.xml
+++ b/lang/java/avro/pom.xml
@@ -169,6 +169,11 @@
joda-time
true
+
+ org.yaml
+ snakeyaml
+ 1.16
+
diff --git a/lang/java/avro/src/main/java/org/apache/avro/Schema.java b/lang/java/avro/src/main/java/org/apache/avro/Schema.java
index 9a201ce45fa..66fa63f1eec 100644
--- a/lang/java/avro/src/main/java/org/apache/avro/Schema.java
+++ b/lang/java/avro/src/main/java/org/apache/avro/Schema.java
@@ -22,6 +22,8 @@
import java.io.InputStream;
import java.io.StringReader;
import java.io.StringWriter;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
@@ -43,6 +45,7 @@
import org.codehaus.jackson.JsonGenerator;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.node.DoubleNode;
+import org.yaml.snakeyaml.Yaml;
/** An abstract data type.
*
A schema may be one of:
@@ -82,6 +85,7 @@ public abstract class Schema extends JsonProperties {
static final ObjectMapper MAPPER = new ObjectMapper(FACTORY);
private static final int NO_HASHCODE = Integer.MIN_VALUE;
+ public static String metadataDirectory = "";
static {
FACTORY.enable(JsonParser.Feature.ALLOW_COMMENTS);
@@ -283,6 +287,16 @@ public boolean isError() {
throw new AvroRuntimeException("Not a record: "+this);
}
+ /** If this is a record and there is a metadata return metadata */
+ public Map getMetadata() {
+ throw new AvroRuntimeException("Not a record: "+this);
+ }
+
+ /** If this is a record set metadata for this record. **/
+ public void setMetadata(Map metadata) {
+ throw new AvroRuntimeException("Not a record: "+this);
+ }
+
/** If this is an array, returns its element type. */
public Schema getElementType() {
throw new AvroRuntimeException("Not an array: "+this);
@@ -605,6 +619,8 @@ private static class RecordSchema extends NamedSchema {
private List fields;
private Map fieldMap;
private final boolean isError;
+ private Map metadata;
+
public RecordSchema(Name name, String doc, boolean isError) {
super(Type.RECORD, name, doc);
this.isError = isError;
@@ -656,6 +672,18 @@ public void setFields(List fields) {
this.fields = ff.lock();
this.hashCode = NO_HASHCODE;
}
+
+ @Override
+ public Map getMetadata() {
+ return metadata;
+ }
+
+ @Override
+ public void setMetadata(Map metadata) {
+ this.metadata = metadata;
+
+ }
+
public boolean equals(Object o) {
if (o == this) return true;
if (!(o instanceof RecordSchema)) return false;
@@ -1249,6 +1277,7 @@ static Schema parse(JsonNode schema, Names names) {
} else if (type.equals("record") || type.equals("error")) { // record
List fields = new ArrayList();
result = new RecordSchema(name, doc, type.equals("error"));
+ result.setMetadata(getMetadataFromRecord(name.name));
if (name != null) names.add(result);
JsonNode fieldsNode = schema.get("fields");
if (fieldsNode == null || !fieldsNode.isArray())
@@ -1343,6 +1372,25 @@ static Schema parse(JsonNode schema, Names names) {
}
}
+ private static Map getMetadataFromRecord(String fileName) {
+ Map map = null;
+ String nameWithExtension = fileName + ".yml";
+ File metadataFileFromRecord = new File(Schema.metadataDirectory + "/" + nameWithExtension);
+
+ if(metadataFileFromRecord.exists()) {
+ try {
+ FileInputStream metadataInputStream = new FileInputStream(metadataFileFromRecord);
+ Yaml yaml = new Yaml();
+ map = (Map) yaml.load(metadataInputStream);
+
+ } catch (FileNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ return map;
+ }
+
private static Set parseAliases(JsonNode node) {
JsonNode aliasesNode = node.get("aliases");
if (aliasesNode == null)
diff --git a/lang/java/avro/src/test/java/org/apache/avro/TestSchema.java b/lang/java/avro/src/test/java/org/apache/avro/TestSchema.java
index ba2cab49d72..17c2cfc3f75 100644
--- a/lang/java/avro/src/test/java/org/apache/avro/TestSchema.java
+++ b/lang/java/avro/src/test/java/org/apache/avro/TestSchema.java
@@ -22,6 +22,8 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import java.io.File;
+import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
@@ -99,5 +101,14 @@ public void testSchemaWithNullFields() {
Schema.createRecord("foobar", null, null, false, null);
}
+ @Test
+ public void testRecordMetadadaLoad() throws IOException {
+ Schema.metadataDirectory = "src/test/resources/record-metadata";
+ File file = new File("src/test/resources/SchemaBuilder.avsc");
+ Schema schema = new Schema.Parser().parse(file);
+
+ assertNotNull(schema);
+ assertNotNull(schema.getMetadata());
+ }
}
diff --git a/lang/java/avro/src/test/resources/record-metadata/recordAll.yml b/lang/java/avro/src/test/resources/record-metadata/recordAll.yml
new file mode 100644
index 00000000000..7b73a9f2a66
--- /dev/null
+++ b/lang/java/avro/src/test/resources/record-metadata/recordAll.yml
@@ -0,0 +1,3 @@
+persistence:
+ db:
+ database: 'foobar'
diff --git a/lang/java/maven-plugin/src/main/java/org/apache/avro/mojo/AbstractAvroMojo.java b/lang/java/maven-plugin/src/main/java/org/apache/avro/mojo/AbstractAvroMojo.java
index a5d8b311f11..278011eea01 100644
--- a/lang/java/maven-plugin/src/main/java/org/apache/avro/mojo/AbstractAvroMojo.java
+++ b/lang/java/maven-plugin/src/main/java/org/apache/avro/mojo/AbstractAvroMojo.java
@@ -18,18 +18,17 @@
package org.apache.avro.mojo;
-import java.io.File;
-import java.io.IOException;
-import java.util.Arrays;
-
import org.apache.avro.compiler.specific.SpecificCompiler;
-
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.project.MavenProject;
import org.apache.maven.shared.model.fileset.FileSet;
import org.apache.maven.shared.model.fileset.util.FileSetManager;
+import java.io.File;
+import java.io.IOException;
+import java.util.Arrays;
+
/**
* Base for Avro Compiler Mojos.
*/
@@ -114,6 +113,13 @@ public abstract class AbstractAvroMojo extends AbstractMojo {
*/
protected String templateDirectory = "/org/apache/avro/compiler/specific/templates/java/classic/";
+ /**
+ * The directory that contains yaml files to be used as Metadata attribute in RecordSchema.
+ *
+ * @parameter property="metadataDirectory"
+ */
+ protected String metadataDirectory = "";
+
/**
* Determines whether or not to create setters for the fields of the record.
* The default is to create setters.
diff --git a/lang/java/maven-plugin/src/main/java/org/apache/avro/mojo/SchemaMojo.java b/lang/java/maven-plugin/src/main/java/org/apache/avro/mojo/SchemaMojo.java
index 7a7eaf96aab..6f8c092722f 100644
--- a/lang/java/maven-plugin/src/main/java/org/apache/avro/mojo/SchemaMojo.java
+++ b/lang/java/maven-plugin/src/main/java/org/apache/avro/mojo/SchemaMojo.java
@@ -18,14 +18,13 @@
package org.apache.avro.mojo;
+import org.apache.avro.Schema;
+import org.apache.avro.compiler.specific.SpecificCompiler;
import org.apache.avro.generic.GenericData.StringType;
import java.io.File;
import java.io.IOException;
-import org.apache.avro.Schema;
-import org.apache.avro.compiler.specific.SpecificCompiler;
-
/**
* Generate Java classes from Avro schema files (.avsc)
*
@@ -61,18 +60,8 @@ public class SchemaMojo extends AbstractAvroMojo {
@Override
protected void doCompile(String filename, File sourceDirectory, File outputDirectory) throws IOException {
File src = new File(sourceDirectory, filename);
- Schema schema;
+ Schema schema = parseSrc(src);
- // This is necessary to maintain backward-compatibility. If there are
- // no imported files then isolate the schemas from each other, otherwise
- // allow them to share a single schema so resuse and sharing of schema
- // is possible.
- if (imports == null) {
- schema = new Schema.Parser().parse(src);
- } else {
- schema = schemaParser.parse(src);
- }
-
SpecificCompiler compiler = new SpecificCompiler(schema);
compiler.setTemplateDir(templateDirectory);
compiler.setStringType(StringType.valueOf(stringType));
@@ -91,4 +80,21 @@ protected String[] getIncludes() {
protected String[] getTestIncludes() {
return testIncludes;
}
+
+ private Schema parseSrc(File src) throws IOException {
+ Schema.metadataDirectory = this.metadataDirectory;
+ Schema schema;
+
+ // This is necessary to maintain backward-compatibility. If there are
+ // no imported files then isolate the schemas from each other, otherwise
+ // allow them to share a single schema so reuse and sharing of schema
+ // is possible.
+ if (imports == null) {
+ schema = new Schema.Parser().parse(src);
+ } else {
+ schema = schemaParser.parse(src);
+ }
+
+ return schema;
+ }
}