/** * Copyright 2016 Hortonworks. * * 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.hortonworks.registries.storage.catalog; import com.fasterxml.jackson.annotation.JsonIgnore; import com.hortonworks.registries.common.Schema; import com.hortonworks.registries.common.exception.ParserException; import com.hortonworks.registries.common.util.ReflectionHelper; import com.hortonworks.registries.storage.exception.StorageException; import com.hortonworks.registries.storage.Storable; import com.hortonworks.registries.storage.StorableKey; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; /** * Default implementations go here */ public abstract class AbstractStorable implements Storable { private static final Logger LOG = LoggerFactory.getLogger(AbstractStorable.class); @JsonIgnore public StorableKey getStorableKey() { return new StorableKey(getNameSpace(), getPrimaryKey()); } /** * Default implementation that will read all the instance variable names using API and * get the value by calling getter method (POJO) convention on it. * <p> * Sometimes for JDBC to work we need an extra layer of transformation , for example see the implementation * in {@code DataSource} which defines a field of type @{code Type} which is enum and not a primitive type as expected * by the JDBC layer, you can call this method and override the fields that needs transformation. * * @return */ public Map<String, Object> toMap() { Set<String> instanceVariableNames = ReflectionHelper.getFieldNamesToTypes(this.getClass()).keySet(); Map<String, Object> fieldToVal = new HashMap<>(); for (String fieldName : instanceVariableNames) { try { Object val = ReflectionHelper.invokeGetter(fieldName, this); fieldToVal.put(fieldName, val); if (LOG.isTraceEnabled()) { LOG.trace("toMap: Adding fieldName {} = {} ", fieldName, val); } } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) { throw new StorageException(e); } } return fieldToVal; } /** * Default implementation that will read all the instance variable names and invoke setter. * <p> * Same as the toMap() method you should override this method when a field's defined type is not a primitive. * * @return */ public Storable fromMap(Map<String, Object> map) { for (Map.Entry<String, Object> entry : map.entrySet()) { try { if (entry.getValue() != null) { ReflectionHelper.invokeSetter(entry.getKey(), this, entry.getValue()); } } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) { throw new StorageException(e); } } return this; } /** * Default implementation that will generate schema by reading all the field names in the class and use its * define type to convert to the Schema type. * * @return */ @JsonIgnore public Schema getSchema() { Map<String, Class> fieldNamesToTypes = ReflectionHelper.getFieldNamesToTypes(this.getClass()); List<Schema.Field> fields = new ArrayList<>(); for (Map.Entry<String, Class> entry : fieldNamesToTypes.entrySet()) { try { Object val = ReflectionHelper.invokeGetter(entry.getKey(), this); Schema.Type type; if (val != null) { type = Schema.fromJavaType(val); } else { type = Schema.fromJavaType(entry.getValue()); } fields.add(new Schema.Field(entry.getKey(), type)); if (LOG.isTraceEnabled()) { LOG.trace("getSchema: Adding {} = {} ", entry.getKey(), type); } } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException | ParserException e) { throw new StorageException(e); } } return Schema.of(fields); } @Override public Long getId() { throw new UnsupportedOperationException("Not implemented"); } @Override public void setId(Long id) { throw new UnsupportedOperationException("Not implemented"); } }