String newSchemaStr) {
// We want to make sure the managed schema map has as recent
// a copy of the managed schema in HBase as possible.
refreshManagedSchemaCache(tableName, entityName);
ManagedSchema managedSchema = getManagedSchema(tableName, entityName);
KeyEntitySchemaParser<?, ?> schemaParser = getSchemaParser(managedSchema
.getSchemaType());
// validate it's a valid avro schema by parsing it
EntitySchema newEntitySchema = schemaParser.parseEntitySchema(newSchemaStr);
KeySchema newKeySchema = schemaParser.parseKeySchema(newSchemaStr);
// verify that the newSchema isn't a duplicate of a previous schema version.
if (hasSchemaVersion(tableName, entityName, newEntitySchema)) {
throw new IncompatibleSchemaException(
"Schema already exists as version: "
+ getEntityVersion(tableName, entityName, newEntitySchema));
}
// validate that, for each version of the schema, this schema is
// compatible with those schema version. That means the field mapping
// hasn't changed, and we can read old schemas, and processes that
// are configured with old schemas can read new schemas.
int greatestSchemaVersion = 0;
for (Entry<String, String> entry : managedSchema.getEntitySchemas()
.entrySet()) {
int version = Integer.parseInt(entry.getKey());
if (version > greatestSchemaVersion) {
greatestSchemaVersion = version;
}
String schemaString = entry.getValue();
KeySchema keySchema = schemaParser.parseKeySchema(schemaString);
EntitySchema entitySchema = schemaParser.parseEntitySchema(schemaString);
if (!newKeySchema.compatible(keySchema)) {
String msg = "StorageKey fields of entity schema not compatible with version "
+ Integer.toString(version)
+ ": Old schema: "
+ schemaString
+ " New schema: " + newEntitySchema.getRawSchema();
throw new IncompatibleSchemaException(msg);
}
if (!newEntitySchema.compatible(entitySchema)) {
String msg = "Avro schema not compatible with version "
+ Integer.toString(version) + ": Old schema: " + schemaString
+ " New schema: " + newEntitySchema.getRawSchema();
throw new IncompatibleSchemaException(msg);
}
}
// Validate that this schema is compatible with other schemas registered
// with this same table.
validateCompatibleWithTableSchemas(tableName, newKeySchema, newEntitySchema);
// at this point, the schema is a valid migration. persist it.
managedSchema.getEntitySchemas().put(
Integer.toString(greatestSchemaVersion + 1),
newEntitySchema.getRawSchema());
if (!managedSchemaDao.save(managedSchema)) {
throw new ConcurrentSchemaModificationException(
"The schema has been updated concurrently.");