/**
* Copyright 2013 Cloudera Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.cloudera.cdk.data.hbase.avro;
import com.cloudera.cdk.data.DatasetException;
import java.lang.reflect.Constructor;
import org.apache.avro.specific.SpecificRecord;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* An AvroRecordBuilderFactory instance that can construct AvroRecordBuilders
* which are able to build Avro SpecificRecord types.
*
* @param <T>
* The type of SpecificRecord this factory creates builders for.
*/
public class SpecificAvroRecordBuilderFactory<T extends SpecificRecord>
implements AvroRecordBuilderFactory<T> {
private static final Logger LOG = LoggerFactory
.getLogger(SpecificAvroRecordBuilderFactory.class);
private final Class<T> recordClass;
private final Constructor<T> recordClassConstructor;
/**
* Construct the factory, giving it the class of the SpecificRecor the
* builders will construct.
*
* @param recordClass
* The class of the SpecificRecords the builders will construct.
*/
public SpecificAvroRecordBuilderFactory(Class<T> recordClass) {
this.recordClass = recordClass;
try {
// Get the constructor of the class so we don't have to
// perform this expensive reflection call for every
// builder constructed.
this.recordClassConstructor = recordClass.getConstructor();
} catch (Exception e) {
// A number of reflection exceptions could be caught here.
// No good way to handle these types of exceptions, so
// throw an DatasetException up to the user.
String msg = "Could not get a default constructor for class: "
+ recordClass.toString();
LOG.error(msg, e);
throw new DatasetException(msg, e);
}
}
/**
* The AvroRecordBuiler instance that can construct SpecificRecords.
*
* @param <T>
* The concrete type of SpecificRecord
*/
private static class SpecificAvroRecordBuilder<T extends SpecificRecord>
implements AvroRecordBuilder<T> {
private T specificRecord;
/**
* Constructor takes the constructor of the SpecificRecord, and will use
* that to create a new SpecificRecord instance.
*
* @param recordConstructor
* The SpecificRecord constructor.
*/
public SpecificAvroRecordBuilder(Constructor<T> recordConstructor) {
try {
specificRecord = recordConstructor.newInstance();
} catch (Exception e) {
// A large number of reflection errors can occur here. There
// is really no proper way to handle this type of error, so
// throw a RuntimeException up to the user.
LOG.error("Could not create a SpecificRecord instance.", e);
throw new RuntimeException(e);
}
}
@Override
public void put(String field, Object value) {
int fieldPos = specificRecord.getSchema().getField(field).pos();
specificRecord.put(fieldPos, value);
}
@Override
public T build() {
return specificRecord;
}
}
@Override
public AvroRecordBuilder<T> getBuilder() {
try {
return new SpecificAvroRecordBuilder<T>(recordClassConstructor);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@Override
public Class<T> getRecordClass() {
return recordClass;
}
}