/* * 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.datamigration; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.commons.dbutils.QueryRunner; import org.apache.commons.dbutils.handlers.BeanListHandler; import org.kaaproject.kaa.server.datamigration.model.EventClass; import org.kaaproject.kaa.server.datamigration.model.EventSchemaVersion; import org.kaaproject.kaa.server.datamigration.model.Schema; import org.kaaproject.kaa.server.datamigration.utils.BaseSchemaIdCounter; import org.kaaproject.kaa.server.datamigration.utils.datadefinition.Constraint; import org.kaaproject.kaa.server.datamigration.utils.datadefinition.ReferenceOptions; import java.io.IOException; import java.sql.Connection; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; public class CtlEventsMigration extends AbstractCtlMigration { private static final String EVENT_SCHEMA_VERSION_TABLE_NAME = "event_schems_versions"; private static final String EVENT_CLASS_FAMILY_TABLE_NAME = "events_class_family"; private static final String EVENT_CLASS_FAMILY_VERSION_TABLE_NAME = "events_class_family_versions"; private static final String EVENT_CLASS_TABLE_NAME = "events_class"; private static final String BASE_SCHEMA_TABLE_NAME = "base_schems"; private static final String APPLICATION_EVENT_MAP_TABLE_NAME = "application_event_map"; public CtlEventsMigration(Connection connection) { super(connection); } //actually not needed here @Override protected String getPrefixTableName() { return null; } @Override public void beforeTransform() throws SQLException { dd.dropUnnamedFk(EVENT_CLASS_TABLE_NAME, EVENT_CLASS_FAMILY_TABLE_NAME); dd.dropUnnamedFk(APPLICATION_EVENT_MAP_TABLE_NAME, EVENT_CLASS_TABLE_NAME); runner.update( connection, "ALTER TABLE " + BASE_SCHEMA_TABLE_NAME + " CHANGE application_id application_id bigint(20)"); } @Override public void afterTransform() throws SQLException { dd.alterTable(EVENT_CLASS_TABLE_NAME) .add(Constraint.constraint("FK_events_class_family_versions_id") .foreignKey("events_class_family_versions_id") .references("events_class_family_versions", "id") .onDelete(ReferenceOptions.CASCADE) .onUpdate(ReferenceOptions.CASCADE)) .execute(); dd.alterTable(APPLICATION_EVENT_MAP_TABLE_NAME) .add(Constraint.constraint("FK_events_class_id") .foreignKey("events_class_id") .references("events_class", "id") .onDelete(ReferenceOptions.CASCADE)) .execute(); } @Override protected List<Schema> transform() throws SQLException { // fetch schemas of appropriate feature like configuration List<Schema> schemas = new ArrayList<>(); final List<EventSchemaVersion> oldEsvs = runner.query(connection, "SELECT id, schems, created_time, created_username FROM " + EVENT_SCHEMA_VERSION_TABLE_NAME, new BeanListHandler<EventSchemaVersion>(EventSchemaVersion.class)); final List<EventClass> oldECs = runner.query(connection, "SELECT id, schems, version FROM " + EVENT_CLASS_TABLE_NAME + " WHERE schems not like '{\"type\":\"enum\"%'", new BeanListHandler<>(EventClass.class)); runner.update( connection, "ALTER TABLE " + EVENT_SCHEMA_VERSION_TABLE_NAME + " DROP COLUMN schems"); runner.update( connection, "ALTER TABLE " + EVENT_SCHEMA_VERSION_TABLE_NAME + " RENAME " + EVENT_CLASS_FAMILY_VERSION_TABLE_NAME); runner.update( connection, "ALTER TABLE " + EVENT_CLASS_TABLE_NAME + " CHANGE events_class_family_id events_class_family_versions_id bigint(20)"); runner.update(connection, "ALTER TABLE " + EVENT_CLASS_TABLE_NAME + " DROP COLUMN schems"); runner.update(connection, "ALTER TABLE " + EVENT_CLASS_TABLE_NAME + " DROP COLUMN version"); for (EventClass ec : oldECs) { updateFamilyVersionId(ec, oldEsvs, runner); } for (EventClass ec : oldECs) { EventSchemaVersion esv = findParent(ec, oldEsvs); Long id = ec.getId(); Long createdTime = esv.getCreatedTime(); String createUsername = esv.getCreatedUsername(); String description = null; String name = parseName(ec.getSchems()); String schems = ec.getSchems(); Integer version = ec.getVersion(); Long applicationId = null; String type = null; //fixme: what is type? Schema schema = new Schema( id, version, name, description, createUsername, createdTime, applicationId, schems, type); schemas.add(schema); } // shift ids in order to avoid PK constraint violation during adding record to base_schema Long shift = runner.query( connection, "select max(id) as max_id from " + EVENT_CLASS_TABLE_NAME, rs -> rs.next() ? rs.getLong("max_id") : null); Long idShift = BaseSchemaIdCounter.getInstance().getAndShift(shift); runner.update( connection, "update " + EVENT_CLASS_TABLE_NAME + " set id = id + " + idShift + " order by id desc"); runner.update( connection, "update " + APPLICATION_EVENT_MAP_TABLE_NAME + " set events_class_id = events_class_id + " + idShift + " order by id desc"); schemas.forEach(s -> s.setId(s.getId() + idShift)); return schemas; } private EventSchemaVersion findParent(EventClass ec, List<EventSchemaVersion> versions) { for (EventSchemaVersion esv : versions) { if (ecBelongToThisFamilyVersion(ec, esv)) { return esv; } } return null; } private void updateFamilyVersionId(EventClass ec, List<EventSchemaVersion> versions, QueryRunner runner) throws SQLException { for (EventSchemaVersion esv : versions) { if (ecBelongToThisFamilyVersion(ec, esv)) { int updateCount = runner.update(this.connection, "UPDATE " + EVENT_CLASS_TABLE_NAME + " SET events_class_family_versions_id=? WHERE id=?", esv.getId(), ec.getId()); if (updateCount != 1) { System.err.println("Error: failed to update event class's reference to ECFV: " + ec); } break; } } } private boolean ecBelongToThisFamilyVersion(EventClass ec, EventSchemaVersion esv) { try { JsonNode jsonNode = new ObjectMapper().readTree(ec.getSchems()); String namespace = jsonNode.get("namespace").asText(); String name = jsonNode.get("name").asText(); return esv.getSchemas().contains(name) && esv.getSchemas().contains(namespace); } catch (IOException ex) { System.err.println("Failed to read EventClass schema: " + ec); } return false; } private String parseName(String body) { try { JsonNode jsonNode = new ObjectMapper().readTree(body); return jsonNode.get("name").asText(); } catch (IOException ex) { System.err.println("Failed to parse name from schema: " + body); } return ""; } }