You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@avro.apache.org by to...@apache.org on 2014/06/09 13:36:35 UTC

svn commit: r1601345 - in /avro/trunk: ./ lang/java/avro/src/main/java/org/apache/avro/ lang/java/avro/src/main/java/org/apache/avro/io/parsing/ lang/java/avro/src/test/java/org/apache/avro/

Author: tomwhite
Date: Mon Jun  9 11:36:34 2014
New Revision: 1601345

URL: http://svn.apache.org/r1601345
Log:
AVRO-1315. Java: Schema Validation utilities. Contributed by scottcarey and tomwhite.

Added:
    avro/trunk/lang/java/avro/src/main/java/org/apache/avro/SchemaValidationException.java
    avro/trunk/lang/java/avro/src/main/java/org/apache/avro/SchemaValidationStrategy.java
    avro/trunk/lang/java/avro/src/main/java/org/apache/avro/SchemaValidator.java
    avro/trunk/lang/java/avro/src/main/java/org/apache/avro/SchemaValidatorBuilder.java
    avro/trunk/lang/java/avro/src/main/java/org/apache/avro/ValidateAll.java
    avro/trunk/lang/java/avro/src/main/java/org/apache/avro/ValidateCanBeRead.java
    avro/trunk/lang/java/avro/src/main/java/org/apache/avro/ValidateCanRead.java
    avro/trunk/lang/java/avro/src/main/java/org/apache/avro/ValidateLatest.java
    avro/trunk/lang/java/avro/src/main/java/org/apache/avro/ValidateMutualRead.java
    avro/trunk/lang/java/avro/src/test/java/org/apache/avro/TestSchemaValidation.java
Modified:
    avro/trunk/CHANGES.txt
    avro/trunk/lang/java/avro/src/main/java/org/apache/avro/io/parsing/Symbol.java

Modified: avro/trunk/CHANGES.txt
URL: http://svn.apache.org/viewvc/avro/trunk/CHANGES.txt?rev=1601345&r1=1601344&r2=1601345&view=diff
==============================================================================
--- avro/trunk/CHANGES.txt (original)
+++ avro/trunk/CHANGES.txt Mon Jun  9 11:36:34 2014
@@ -4,6 +4,8 @@ Trunk (not yet released)
 
   NEW FEATURES
 
+    AVRO-1315. Java: Schema Validation utilities. (scottcarey and tomwhite)
+
     AVRO-1439. Java: Add AvroMultipleInputs for mapred. (Harsh J via cutting)
 
     AVRO-974. Add a Perl implementation of Avro. (Yann Kerhervé & John Karp)

Added: avro/trunk/lang/java/avro/src/main/java/org/apache/avro/SchemaValidationException.java
URL: http://svn.apache.org/viewvc/avro/trunk/lang/java/avro/src/main/java/org/apache/avro/SchemaValidationException.java?rev=1601345&view=auto
==============================================================================
--- avro/trunk/lang/java/avro/src/main/java/org/apache/avro/SchemaValidationException.java (added)
+++ avro/trunk/lang/java/avro/src/main/java/org/apache/avro/SchemaValidationException.java Mon Jun  9 11:36:34 2014
@@ -0,0 +1,39 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.  See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package org.apache.avro;
+
+/**
+ * Thrown when {@link SchemaValidator} fails to validate a schema.
+ */
+public class SchemaValidationException extends Exception {
+
+  public SchemaValidationException(Schema reader, Schema writer) {
+    super(getMessage(reader, writer));
+  }
+
+  public SchemaValidationException(Schema reader, Schema writer,
+      Throwable cause) {
+    super(getMessage(reader, writer), cause);
+  }
+
+  private static String getMessage(Schema reader, Schema writer) {
+    return "Unable to read schema: \n"
+        + writer.toString(true) + "\nusing schema:\n" + reader.toString(true);
+  }
+}

Added: avro/trunk/lang/java/avro/src/main/java/org/apache/avro/SchemaValidationStrategy.java
URL: http://svn.apache.org/viewvc/avro/trunk/lang/java/avro/src/main/java/org/apache/avro/SchemaValidationStrategy.java?rev=1601345&view=auto
==============================================================================
--- avro/trunk/lang/java/avro/src/main/java/org/apache/avro/SchemaValidationStrategy.java (added)
+++ avro/trunk/lang/java/avro/src/main/java/org/apache/avro/SchemaValidationStrategy.java Mon Jun  9 11:36:34 2014
@@ -0,0 +1,20 @@
+package org.apache.avro;
+
+/**
+ * An interface for validating the compatibility of a single schema against
+ * another.
+ * <p>
+ * What makes one schema compatible with another is not defined by the contract.
+ * <p/>
+ */
+public interface SchemaValidationStrategy {
+
+  /**
+   * Validates that one schema is compatible with another.
+   * 
+   * @throws SchemaValidationException if the schemas are not compatible.
+   */
+  void validate(Schema toValidate, Schema existing)
+      throws SchemaValidationException;
+
+}

Added: avro/trunk/lang/java/avro/src/main/java/org/apache/avro/SchemaValidator.java
URL: http://svn.apache.org/viewvc/avro/trunk/lang/java/avro/src/main/java/org/apache/avro/SchemaValidator.java?rev=1601345&view=auto
==============================================================================
--- avro/trunk/lang/java/avro/src/main/java/org/apache/avro/SchemaValidator.java (added)
+++ avro/trunk/lang/java/avro/src/main/java/org/apache/avro/SchemaValidator.java Mon Jun  9 11:36:34 2014
@@ -0,0 +1,46 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.avro;
+
+/**
+ * <p>
+ * A SchemaValidator has one method, which validates that a {@link Schema} is
+ * <b>compatible<b/> with the other schemas provided.
+ * </p>
+ * <p>
+ * What makes one Schema compatible with another is not part of the interface
+ * contract.
+ * </p>
+ */
+public interface SchemaValidator {
+
+  /**
+   * Validate one schema against others. The order of the schemas to validate
+   * against is chronological from most recent to oldest, if there is a natural
+   * chronological order. This allows some validators to identify which schemas
+   * are the most "recent" in order to validate only against the mosst recent
+   * schema(s).
+   * 
+   * @param toValidate The schema to validate
+   * @param existing The schemas to validate against, in order from most recent to latest if applicable
+   * @throws SchemaValidationException if the schema fails to validate.
+   */
+  void validate(Schema toValidate, Iterable<Schema> existing) throws SchemaValidationException;
+
+}

Added: avro/trunk/lang/java/avro/src/main/java/org/apache/avro/SchemaValidatorBuilder.java
URL: http://svn.apache.org/viewvc/avro/trunk/lang/java/avro/src/main/java/org/apache/avro/SchemaValidatorBuilder.java?rev=1601345&view=auto
==============================================================================
--- avro/trunk/lang/java/avro/src/main/java/org/apache/avro/SchemaValidatorBuilder.java (added)
+++ avro/trunk/lang/java/avro/src/main/java/org/apache/avro/SchemaValidatorBuilder.java Mon Jun  9 11:36:34 2014
@@ -0,0 +1,77 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.avro;
+
+/**
+ * <p>
+ * A Builder for creating SchemaValidators.
+ * </p>
+ */
+public final class SchemaValidatorBuilder {
+  private SchemaValidationStrategy strategy;
+
+  public SchemaValidatorBuilder strategy(SchemaValidationStrategy strategy) {
+    this.strategy = strategy;
+    return this;
+  }
+
+  /**
+   * Use a strategy that validates that a schema can be used to read existing
+   * schema(s) according to the Avro default schema resolution.
+   */
+  public SchemaValidatorBuilder canReadStrategy() {
+    this.strategy = new ValidateCanRead();
+    return this;
+  }
+
+  /**
+   * Use a strategy that validates that a schema can be read by existing
+   * schema(s) according to the Avro default schema resolution.
+   */
+  public SchemaValidatorBuilder canBeReadStrategy() {
+    this.strategy = new ValidateCanBeRead();
+    return this;
+  }
+
+  /**
+   * Use a strategy that validates that a schema can read existing schema(s),
+   * and vice-versa, according to the Avro default schema resolution.
+   */
+  public SchemaValidatorBuilder mutualReadStrategy() {
+    this.strategy = new ValidateMutualRead();
+    return this;
+  }
+  
+  public SchemaValidator validateLatest() {
+    valid();
+    return new ValidateLatest(strategy);
+  }
+  
+  public SchemaValidator validateAll() {
+    valid();
+    return new ValidateAll(strategy);
+  }
+  
+  private void valid() {
+    if(null == strategy) {
+      throw new AvroRuntimeException("SchemaValidationStrategy not specified in builder");
+    }
+  }
+
+}

Added: avro/trunk/lang/java/avro/src/main/java/org/apache/avro/ValidateAll.java
URL: http://svn.apache.org/viewvc/avro/trunk/lang/java/avro/src/main/java/org/apache/avro/ValidateAll.java?rev=1601345&view=auto
==============================================================================
--- avro/trunk/lang/java/avro/src/main/java/org/apache/avro/ValidateAll.java (added)
+++ avro/trunk/lang/java/avro/src/main/java/org/apache/avro/ValidateAll.java Mon Jun  9 11:36:34 2014
@@ -0,0 +1,55 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.  See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package org.apache.avro;
+
+import java.util.Iterator;
+
+/**
+ * <p>
+ * A {@link SchemaValidator} for validating the provided schema against all
+ * schemas in the Iterable in {@link #validate(Schema, Iterable)}.
+ * </p>
+ * <p>
+ * Uses the {@link SchemaValidationStrategy} provided in the constructor to
+ * validate the {@link Schema} against each Schema in the Iterable, in Iterator
+ * order, via {@link SchemaValidationStrategy#validate(Schema, Schema)}.
+ * </p>
+ */
+public final class ValidateAll implements SchemaValidator {
+  private final SchemaValidationStrategy strategy;
+
+  /**
+   * @param strategy
+   *          The strategy to use for validation of pairwise schemas.
+   */
+  public ValidateAll(SchemaValidationStrategy strategy) {
+    this.strategy = strategy;
+  }
+
+  @Override
+  public void validate(Schema toValidate, Iterable<Schema> schemasInOrder)
+      throws SchemaValidationException {
+    Iterator<Schema> schemas = schemasInOrder.iterator();
+    while (schemas.hasNext()) {
+      Schema existing = schemas.next();
+      strategy.validate(toValidate, existing);
+    }
+  }
+
+}

Added: avro/trunk/lang/java/avro/src/main/java/org/apache/avro/ValidateCanBeRead.java
URL: http://svn.apache.org/viewvc/avro/trunk/lang/java/avro/src/main/java/org/apache/avro/ValidateCanBeRead.java?rev=1601345&view=auto
==============================================================================
--- avro/trunk/lang/java/avro/src/main/java/org/apache/avro/ValidateCanBeRead.java (added)
+++ avro/trunk/lang/java/avro/src/main/java/org/apache/avro/ValidateCanBeRead.java Mon Jun  9 11:36:34 2014
@@ -0,0 +1,42 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.  See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package org.apache.avro;
+
+/**
+ * A {@link SchemaValidationStrategy} that checks that the data written with the
+ * {@link Schema} to validate can be read by the existing schema according to
+ * the default Avro schema resolution rules.
+ * 
+ */
+class ValidateCanBeRead implements SchemaValidationStrategy {
+
+  /**
+   * Validate that data written with first schema provided can be read using the
+   * second schema, according to the default Avro schema resolution rules.
+   * 
+   * @throws SchemaValidationException
+   *           if the second schema cannot read data written by the first.
+   */
+  @Override
+  public void validate(Schema toValidate, Schema existing)
+      throws SchemaValidationException {
+    ValidateMutualRead.canRead(toValidate, existing);
+  }
+
+}

Added: avro/trunk/lang/java/avro/src/main/java/org/apache/avro/ValidateCanRead.java
URL: http://svn.apache.org/viewvc/avro/trunk/lang/java/avro/src/main/java/org/apache/avro/ValidateCanRead.java?rev=1601345&view=auto
==============================================================================
--- avro/trunk/lang/java/avro/src/main/java/org/apache/avro/ValidateCanRead.java (added)
+++ avro/trunk/lang/java/avro/src/main/java/org/apache/avro/ValidateCanRead.java Mon Jun  9 11:36:34 2014
@@ -0,0 +1,43 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.  See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package org.apache.avro;
+
+/**
+ * A {@link SchemaValidationStrategy} that checks that the {@link Schema} to
+ * validate can read the existing schema according to the default Avro schema
+ * resolution rules.
+ * 
+ */
+class ValidateCanRead implements SchemaValidationStrategy {
+
+  /**
+   * Validate that the first schema provided can be used to read data written
+   * with the second schema, according to the default Avro schema resolution
+   * rules.
+   * 
+   * @throws SchemaValidationException
+   *           if the first schema cannot read data written by the second.
+   */
+  @Override
+  public void validate(Schema toValidate, Schema existing)
+      throws SchemaValidationException {
+    ValidateMutualRead.canRead(existing, toValidate);
+  }
+
+}

Added: avro/trunk/lang/java/avro/src/main/java/org/apache/avro/ValidateLatest.java
URL: http://svn.apache.org/viewvc/avro/trunk/lang/java/avro/src/main/java/org/apache/avro/ValidateLatest.java?rev=1601345&view=auto
==============================================================================
--- avro/trunk/lang/java/avro/src/main/java/org/apache/avro/ValidateLatest.java (added)
+++ avro/trunk/lang/java/avro/src/main/java/org/apache/avro/ValidateLatest.java Mon Jun  9 11:36:34 2014
@@ -0,0 +1,55 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.  See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package org.apache.avro;
+
+import java.util.Iterator;
+
+/**
+ * <p>
+ * A {@link SchemaValidator} for validating the provided schema against the
+ * first {@link Schema} in the iterable in {@link #validate(Schema, Iterable)}.
+ * </p>
+ * <p>
+ * Uses the {@link SchemaValidationStrategy} provided in the constructor to
+ * validate the schema against the first Schema in the iterable, if it exists,
+ * via {@link SchemaValidationStrategy#validate(Schema, Schema)}.
+ * </p>
+ */
+public final class ValidateLatest implements SchemaValidator {
+  private final SchemaValidationStrategy strategy;
+
+  /**
+   * @param strategy
+   *          The strategy to use for validation of pairwise schemas.
+   */
+  public ValidateLatest(SchemaValidationStrategy strategy) {
+    this.strategy = strategy;
+  }
+
+  @Override
+  public void validate(Schema toValidate, Iterable<Schema> schemasInOrder)
+      throws SchemaValidationException {
+    Iterator<Schema> schemas = schemasInOrder.iterator();
+    if (schemas.hasNext()) {
+      Schema existing = schemas.next();
+      strategy.validate(toValidate, existing);
+    }
+  }
+
+}

Added: avro/trunk/lang/java/avro/src/main/java/org/apache/avro/ValidateMutualRead.java
URL: http://svn.apache.org/viewvc/avro/trunk/lang/java/avro/src/main/java/org/apache/avro/ValidateMutualRead.java?rev=1601345&view=auto
==============================================================================
--- avro/trunk/lang/java/avro/src/main/java/org/apache/avro/ValidateMutualRead.java (added)
+++ avro/trunk/lang/java/avro/src/main/java/org/apache/avro/ValidateMutualRead.java Mon Jun  9 11:36:34 2014
@@ -0,0 +1,74 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.  See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package org.apache.avro;
+
+import java.io.IOException;
+
+import org.apache.avro.io.parsing.ResolvingGrammarGenerator;
+import org.apache.avro.io.parsing.Symbol;
+
+/**
+ * A {@link SchemaValidationStrategy} that checks that the {@link Schema} to
+ * validate and the existing schema can mutually read each other according to
+ * the default Avro schema resolution rules.
+ * 
+ */
+class ValidateMutualRead implements SchemaValidationStrategy {
+
+  /**
+   * Validate that the schemas provided can mutually read data written by each
+   * other according to the default Avro schema resolution rules.
+   * 
+   * @throws SchemaValidationException if the schemas are not mutually compatible.
+   */
+  @Override
+  public void validate(Schema toValidate, Schema existing)
+      throws SchemaValidationException {
+    canRead(toValidate, existing);
+    canRead(existing, toValidate);
+  }
+
+  /**
+   * Validates that data written with one schema can be read using another,
+   * based on the default Avro schema resolution rules.
+   * 
+   * @param writtenWith
+   *          The "writer's" schema, representing data to be read.
+   * @param readUsing
+   *          The "reader's" schema, representing how the reader will interpret
+   *          data.
+   * @throws SchemaValidationException
+   *           if the schema <b>readUsing<b/> cannot be used to read data
+   *           written with <b>writtenWith<b/>
+   */
+  static void canRead(Schema writtenWith, Schema readUsing)
+      throws SchemaValidationException {
+    boolean error;
+    try {
+      error = Symbol.hasErrors(new ResolvingGrammarGenerator().generate(
+          writtenWith, readUsing));
+    } catch (IOException e) {
+      throw new SchemaValidationException(readUsing, writtenWith, e);
+    }
+    if (error) {
+      throw new SchemaValidationException(readUsing, writtenWith);
+    }
+  }
+
+}

Modified: avro/trunk/lang/java/avro/src/main/java/org/apache/avro/io/parsing/Symbol.java
URL: http://svn.apache.org/viewvc/avro/trunk/lang/java/avro/src/main/java/org/apache/avro/io/parsing/Symbol.java?rev=1601345&r1=1601344&r2=1601345&view=diff
==============================================================================
--- avro/trunk/lang/java/avro/src/main/java/org/apache/avro/io/parsing/Symbol.java (original)
+++ avro/trunk/lang/java/avro/src/main/java/org/apache/avro/io/parsing/Symbol.java Mon Jun  9 11:36:34 2014
@@ -349,6 +349,45 @@ public abstract class Symbol {
     }
 
   }
+  
+  /**
+   * Returns true if the Parser contains any Error symbol, indicating that it may fail 
+   * for some inputs.
+   */
+  public static boolean hasErrors(Symbol symbol) {
+    switch(symbol.kind) {
+    case ALTERNATIVE:
+      return hasErrors(symbol, ((Alternative) symbol).symbols);
+    case EXPLICIT_ACTION:
+      return false;
+    case IMPLICIT_ACTION:
+      return symbol instanceof ErrorAction;
+    case REPEATER:
+      Repeater r = (Repeater) symbol;
+      return hasErrors(r.end) || hasErrors(symbol, r.production);
+    case ROOT:
+    case SEQUENCE:
+      return hasErrors(symbol, symbol.production);
+    case TERMINAL:
+      return false;
+    default:
+      throw new RuntimeException("unknown symbol kind: " + symbol.kind);
+    }
+  }
+  
+  private static boolean hasErrors(Symbol root, Symbol[] symbols) {
+    if(null != symbols) {
+      for(Symbol s: symbols) {
+        if (s == root) {
+          continue;
+        }
+        if (hasErrors(s)) {
+          return true;
+        }
+      }
+    }
+    return false;
+  }
     
   public static class Alternative extends Symbol {
     public final Symbol[] symbols;

Added: avro/trunk/lang/java/avro/src/test/java/org/apache/avro/TestSchemaValidation.java
URL: http://svn.apache.org/viewvc/avro/trunk/lang/java/avro/src/test/java/org/apache/avro/TestSchemaValidation.java?rev=1601345&view=auto
==============================================================================
--- avro/trunk/lang/java/avro/src/test/java/org/apache/avro/TestSchemaValidation.java (added)
+++ avro/trunk/lang/java/avro/src/test/java/org/apache/avro/TestSchemaValidation.java Mon Jun  9 11:36:34 2014
@@ -0,0 +1,147 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.  See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package org.apache.avro;
+
+import java.util.ArrayList;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class TestSchemaValidation {
+
+  SchemaValidatorBuilder builder = new SchemaValidatorBuilder();
+
+  Schema rec = SchemaBuilder.record("test.Rec").fields()
+      .name("a").type().intType().intDefault(1)
+      .name("b").type().longType().noDefault()
+      .endRecord();
+
+  Schema rec2 = SchemaBuilder.record("test.Rec").fields()
+      .name("a").type().intType().intDefault(1)
+      .name("b").type().longType().noDefault()
+      .name("c").type().intType().intDefault(0)
+      .endRecord();
+
+  Schema rec3 = SchemaBuilder.record("test.Rec").fields()
+      .name("b").type().longType().noDefault()
+      .name("c").type().intType().intDefault(0)
+      .endRecord();
+
+  Schema rec4 = SchemaBuilder.record("test.Rec").fields()
+      .name("b").type().longType().noDefault()
+      .name("c").type().intType().noDefault()
+      .endRecord();
+
+  Schema rec5 = SchemaBuilder.record("test.Rec").fields()
+      .name("a").type().stringType().stringDefault("") // different type from original
+      .name("b").type().longType().noDefault()
+      .name("c").type().intType().intDefault(0)
+      .endRecord();
+
+  @Test
+  public void testAllTypes() throws SchemaValidationException {
+    Schema s = SchemaBuilder.record("r").fields()
+        .requiredBoolean("boolF")
+        .requiredInt("intF")
+        .requiredLong("longF")
+        .requiredFloat("floatF")
+        .requiredDouble("doubleF")
+        .requiredString("stringF")
+        .requiredBytes("bytesF")
+        .name("fixedF1").type().fixed("F1").size(1).noDefault()
+        .name("enumF").type().enumeration("E1").symbols("S").noDefault()
+        .name("mapF").type().map().values().stringType().noDefault()
+        .name("arrayF").type().array().items().stringType().noDefault()
+        .name("recordF").type().record("inner").fields()
+        .name("f").type().intType().noDefault()
+        .endRecord().noDefault()
+        .optionalBoolean("boolO")
+        .endRecord();
+    testValidatorPasses(builder.mutualReadStrategy().validateLatest(), s, s);
+  }
+
+  @Test
+  public void testReadOnePrior() throws SchemaValidationException {
+    testValidatorPasses(builder.canReadStrategy().validateLatest(), rec3, rec);
+    testValidatorPasses(builder.canReadStrategy().validateLatest(), rec5, rec3);
+    testValidatorFails(builder.canReadStrategy().validateLatest(), rec4, rec);
+  }
+
+  @Test
+  public void testReadAllPrior() throws SchemaValidationException {
+    testValidatorPasses(builder.canReadStrategy().validateAll(), rec3, rec, rec2);
+    testValidatorFails(builder.canReadStrategy().validateAll(), rec4, rec, rec2, rec3);
+    testValidatorFails(builder.canReadStrategy().validateAll(), rec5, rec, rec2, rec3);
+  }
+
+  @Test
+  public void testOnePriorCanRead() throws SchemaValidationException {
+    testValidatorPasses(builder.canBeReadStrategy().validateLatest(), rec, rec3);
+    testValidatorFails(builder.canBeReadStrategy().validateLatest(), rec, rec4);
+  }
+
+  @Test
+  public void testAllPriorCanRead() throws SchemaValidationException {
+    testValidatorPasses(builder.canBeReadStrategy().validateAll(), rec, rec3, rec2);
+    testValidatorFails(builder.canBeReadStrategy().validateAll(), rec, rec4, rec3, rec2);
+  }
+
+  @Test
+  public void testOnePriorCompatible() throws SchemaValidationException {
+    testValidatorPasses(builder.mutualReadStrategy().validateLatest(), rec, rec3);
+    testValidatorFails(builder.mutualReadStrategy().validateLatest(), rec, rec4);
+  }
+
+  @Test
+  public void testAllPriorCompatible() throws SchemaValidationException {
+    testValidatorPasses(builder.mutualReadStrategy().validateAll(), rec, rec3, rec2);
+    testValidatorFails(builder.mutualReadStrategy().validateAll(), rec, rec4, rec3, rec2);
+  }
+
+  @Test(expected=AvroRuntimeException.class)
+  public void testInvalidBuild() {
+    builder.strategy(null).validateAll();
+  }
+
+  private void testValidatorPasses(SchemaValidator validator,
+      Schema schema, Schema... prev) throws SchemaValidationException {
+    ArrayList<Schema> prior = new ArrayList<Schema>();
+    for(int i = prev.length - 1; i >= 0; i--) {
+      prior.add(prev[i]);
+    }
+    validator.validate(schema, prior);
+  }
+
+  private void testValidatorFails(SchemaValidator validator,
+      Schema schemaFails, Schema... prev) throws SchemaValidationException {
+    ArrayList<Schema> prior = new ArrayList<Schema>();
+    for(int i = prev.length - 1; i >= 0; i--) {
+      prior.add(prev[i]);
+    }
+    boolean threw = false;
+    try {
+      // should fail
+      validator.validate(schemaFails, prior);
+    } catch (SchemaValidationException sve) {
+      threw = true;
+    }
+    Assert.assertTrue(threw);
+  }
+
+}