/* * Copyright 2014-2016 CyberVision, 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 org.kaaproject.kaa.server.admin.services.dao; import org.apache.avro.Schema; import org.apache.avro.generic.GenericRecord; import org.apache.avro.specific.SpecificRecordBase; import org.hibernate.Criteria; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.criterion.Restrictions; import org.kaaproject.avro.ui.converter.FormAvroConverter; import org.kaaproject.avro.ui.shared.RecordField; import org.kaaproject.kaa.common.avro.AvroByteArrayConverter; import org.kaaproject.kaa.common.avro.AvroJsonConverter; import org.kaaproject.kaa.common.avro.GenericAvroConverter; import org.kaaproject.kaa.server.admin.services.entity.Properties; import org.kaaproject.kaa.server.admin.shared.properties.PropertiesDto; import org.kaaproject.kaa.server.common.core.algorithms.generation.DefaultRecordGenerationAlgorithm; import org.kaaproject.kaa.server.common.core.algorithms.generation.DefaultRecordGenerationAlgorithmImpl; import org.kaaproject.kaa.server.common.core.configuration.RawData; import org.kaaproject.kaa.server.common.core.configuration.RawDataFactory; import org.kaaproject.kaa.server.common.core.schema.RawSchema; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Repository; import org.springframework.transaction.annotation.Transactional; import java.io.IOException; @Repository("propertiesFacade") @Transactional("admin") public class PropertiesFacade { private static final Logger LOG = LoggerFactory.getLogger(PropertiesFacade.class); private static final String SCHEMA = "SCHEMA$"; private static final String FQN_PROPERTY = "fqn"; @Autowired private SessionFactory adminSessionFactory; private Session getSession() { return adminSessionFactory.getCurrentSession(); } private Criteria getCriteria() { return getSession().createCriteria(Properties.class); } /** * Build and return properties by specific properties class. * * @param propertiesClass the class of properties * @param <S> the class of properties * @return property instance */ public <S extends SpecificRecordBase> S getSpecificProperties(Class<S> propertiesClass) { Properties entity = findOrCreateByClass(propertiesClass); S specificProperties = null; if (entity != null) { AvroByteArrayConverter<S> converter = new AvroByteArrayConverter<>(propertiesClass); try { specificProperties = converter.fromByteArray(entity.getRawConfiguration()); } catch (IOException ex) { LOG.error( "Unable to parse raw data for specific record " + propertiesClass.getSimpleName(), ex); } } if (specificProperties == null) { specificProperties = buildDefaultProperties(propertiesClass); } return specificProperties; } public <S extends SpecificRecordBase> PropertiesDto getPropertiesDto(Class<S> propertiesClass) throws Exception { Properties entity = findOrCreateByClass(propertiesClass); return toDto(entity, propertiesClass); } /** * Found property from database or create new if not found, update it and save to database. * * @param propertiesDto the property to update * @param propertiesClass the property class * @param <S> the property class * @return property dto */ public <S extends SpecificRecordBase> PropertiesDto editPropertiesDto( PropertiesDto propertiesDto,Class<S> propertiesClass) throws Exception { Properties entity = findOrCreateByClass(propertiesClass); GenericRecord record = FormAvroConverter.createGenericRecordFromRecordField( propertiesDto.getConfiguration()); GenericAvroConverter<GenericRecord> converter = new GenericAvroConverter<>(record.getSchema()); byte[] rawConfiguration = converter.encode(record); entity.setRawConfiguration(rawConfiguration); save(entity); return toDto(entity, propertiesClass); } private Long save(Properties properties) { properties = (Properties) getSession().merge(properties); return properties.getId(); } private <S extends SpecificRecordBase> Properties findOrCreateByClass(Class<S> propertiesClass) { Criteria criteria = getCriteria(); criteria.add(Restrictions.eq(FQN_PROPERTY, propertiesClass.getName())); Properties result = (Properties) criteria.uniqueResult(); if (result != null) { return result; } else { return createDefault(propertiesClass); } } private <S extends SpecificRecordBase> S buildDefaultProperties(Class<S> propertiesClass) { S result = null; try { Schema schema = (Schema) propertiesClass.getField(SCHEMA).get(null); RawSchema rawSchema = new RawSchema(schema.toString()); DefaultRecordGenerationAlgorithm<RawData> algotithm = new DefaultRecordGenerationAlgorithmImpl<>(rawSchema, new RawDataFactory()); RawData rawData = algotithm.getRootData(); AvroJsonConverter<S> converter = new AvroJsonConverter<>(schema, propertiesClass); result = converter.decodeJson(rawData.getRawData()); } catch (Exception ex) { LOG.error( "Unable to build default specific properties for class " + propertiesClass.getSimpleName(), ex); } return result; } private <S extends SpecificRecordBase> Properties createDefault(Class<S> propertiesClass) { Properties properties = new Properties(); S specificProperties = buildDefaultProperties(propertiesClass); AvroByteArrayConverter<S> converter = new AvroByteArrayConverter<>(propertiesClass); try { properties.setRawConfiguration(converter.toByteArray(specificProperties)); } catch (IOException ex) { LOG.error("Unable to serialize configuration for properties", ex); } properties.setFqn(propertiesClass.getName()); Long id = save(properties); properties.setId(id); return properties; } private <S extends SpecificRecordBase> PropertiesDto toDto(Properties entity, Class<S> propertiesClass) throws Exception { PropertiesDto propertiesDto = new PropertiesDto(); propertiesDto.setId(String.valueOf(entity.getId())); Schema schema = (Schema) propertiesClass.getField(SCHEMA).get(null); GenericAvroConverter<GenericRecord> converter = new GenericAvroConverter<>(schema); GenericRecord record = converter.decodeBinary(entity.getRawConfiguration()); RecordField configuration = FormAvroConverter.createRecordFieldFromGenericRecord(record); propertiesDto.setConfiguration(configuration); return propertiesDto; } }