co.cask.common.internal.io.ASMDatumWriterFactory Maven / Gradle / Ivy
/*
* Copyright © 2014 Cask Data, 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 co.cask.common.internal.io;
import co.cask.common.internal.asm.ByteCodeClassLoader;
import co.cask.common.internal.asm.ClassDefinition;
import com.google.common.base.Objects;
import com.google.common.base.Throwables;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.Maps;
import com.google.common.reflect.TypeToken;
import java.util.Map;
import javax.inject.Inject;
/**
* A factory class for creating {@link DatumWriter} instance for different data type and schema.
* It serves as an in memory cache for generated {@link DatumWriter} {@link Class} using ASM.
*/
public final class ASMDatumWriterFactory implements DatumWriterFactory {
private final LoadingCache>> datumWriterClasses;
private final FieldAccessorFactory fieldAccessorFactory;
@Inject
public ASMDatumWriterFactory(FieldAccessorFactory fieldAccessorFactory) {
this.fieldAccessorFactory = fieldAccessorFactory;
this.datumWriterClasses = CacheBuilder.newBuilder().build(new ASMCacheLoader());
}
/**
* Creates a {@link DatumWriter} that is able to encode given data type with the given {@link Schema}.
* The instance created is thread safe and reusable.
*
* @param type Type information of the data type to be encoded.
* @param schema Schema of the data type.
* @param Type of the data type.
* @return A {@link DatumWriter} instance.
*/
@SuppressWarnings("unchecked")
@Override
public DatumWriter create(TypeToken type, Schema schema) {
try {
Class> writerClass = datumWriterClasses.getUnchecked(new CacheKey(schema, type));
return (DatumWriter) writerClass.getConstructor(Schema.class, FieldAccessorFactory.class)
.newInstance(schema, fieldAccessorFactory);
} catch (Exception e) {
throw Throwables.propagate(e);
}
}
/**
* A private {@link com.google.common.cache.CacheLoader} for generating different {@link DatumWriter} {@link Class}.
*/
private static final class ASMCacheLoader extends CacheLoader>> {
private final Map, ByteCodeClassLoader> classloaders = Maps.newIdentityHashMap();
@SuppressWarnings("unchecked")
@Override
public Class> load(CacheKey key) throws Exception {
ClassDefinition classDef = new DatumWriterGenerator().generate(key.getType(), key.getSchema());
ByteCodeClassLoader classloader = classloaders.get(key.getType());
if (classloader == null) {
// The ClassLoader of the generated DatumWriter has CDAP system ClassLoader as parent.
// The ClassDefinition contains list of classes that should not be loaded by the generated class ClassLoader
classloader = new ByteCodeClassLoader(ASMDatumWriterFactory.class.getClassLoader());
classloaders.put(key.getType(), classloader);
}
return (Class>) classloader.addClass(classDef).loadClass(classDef.getClassName());
}
}
private static final class CacheKey {
private final Schema schema;
private final TypeToken> type;
private CacheKey(Schema schema, TypeToken> type) {
this.schema = schema;
this.type = type;
}
public Schema getSchema() {
return schema;
}
public TypeToken> getType() {
return type;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
CacheKey cacheKey = (CacheKey) o;
return schema.equals(cacheKey.schema) && type.equals(cacheKey.type);
}
@Override
public int hashCode() {
return Objects.hashCode(schema, type);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy