| /** |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you 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.apache.ambari.server.view; |
| |
| import org.apache.ambari.server.orm.entities.ViewInstanceEntity; |
| import org.apache.ambari.view.PersistenceException; |
| import org.apache.ambari.view.ViewInstanceDefinition; |
| import org.apache.ambari.view.migration.ViewDataMigrationContext; |
| import org.apache.ambari.view.migration.ViewDataMigrationException; |
| import org.apache.ambari.view.migration.ViewDataMigrator; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| import java.util.Map; |
| |
| /** |
| * Helper class for view data migration. |
| */ |
| public class ViewDataMigrationUtility { |
| |
| /** |
| * The logger. |
| */ |
| protected final static Logger LOG = LoggerFactory.getLogger(ViewDataMigrationUtility.class); |
| |
| /** |
| * The View Registry. |
| */ |
| private ViewRegistry viewRegistry; |
| |
| /** |
| * Constructor. |
| * @param viewRegistry the view registry |
| */ |
| public ViewDataMigrationUtility(ViewRegistry viewRegistry) { |
| this.viewRegistry = viewRegistry; |
| } |
| |
| /** |
| * Migrates data from source to target instance |
| * @param targetInstanceDefinition target instance entity |
| * @param sourceInstanceDefinition source instance entity |
| * @param migrateOnce cancel if previously migrated |
| * |
| * @throws ViewDataMigrationException when view does not support migration or an error during migration occurs. |
| */ |
| public void migrateData(ViewInstanceEntity targetInstanceDefinition, ViewInstanceEntity sourceInstanceDefinition, |
| boolean migrateOnce) |
| throws ViewDataMigrationException { |
| ViewDataMigrationContextImpl migrationContext = getViewDataMigrationContext(targetInstanceDefinition, sourceInstanceDefinition); |
| |
| if (migrateOnce) { |
| if (!isTargetEmpty(migrationContext)) { |
| LOG.error("Migration canceled because target instance is not empty"); |
| return; |
| } |
| } |
| |
| ViewDataMigrator dataMigrator = getViewDataMigrator(targetInstanceDefinition, migrationContext); |
| |
| LOG.debug("Running before-migration hook"); |
| if (!dataMigrator.beforeMigration()) { |
| String msg = "View " + targetInstanceDefinition.getInstanceName() + " canceled the migration process"; |
| |
| LOG.error(msg); |
| throw new ViewDataMigrationException(msg); |
| } |
| |
| Map<String, Class> originClasses = migrationContext.getOriginEntityClasses(); |
| Map<String, Class> currentClasses = migrationContext.getCurrentEntityClasses(); |
| for (Map.Entry<String, Class> originEntity : originClasses.entrySet()) { |
| LOG.debug("Migrating persistence entity " + originEntity.getKey()); |
| if (currentClasses.containsKey(originEntity.getKey())) { |
| Class entity = currentClasses.get(originEntity.getKey()); |
| dataMigrator.migrateEntity(originEntity.getValue(), entity); |
| } else { |
| LOG.debug("Entity " + originEntity.getKey() + " not found in target view"); |
| dataMigrator.migrateEntity(originEntity.getValue(), null); |
| } |
| } |
| |
| LOG.debug("Migrating instance data"); |
| dataMigrator.migrateInstanceData(); |
| |
| LOG.debug("Running after-migration hook"); |
| dataMigrator.afterMigration(); |
| |
| LOG.debug("Copying user permissions"); |
| viewRegistry.copyPrivileges(sourceInstanceDefinition, targetInstanceDefinition); |
| |
| migrationContext.putCurrentInstanceData("upgrade", "upgradedFrom", sourceInstanceDefinition.getViewEntity().getVersion()); |
| |
| migrationContext.closeMigration(); |
| } |
| |
| private boolean isTargetEmpty(ViewDataMigrationContext migrationContext) { |
| if (migrationContext.getCurrentInstanceDataByUser().size() > 0) { |
| return false; |
| } |
| |
| try { |
| for (Class entity : migrationContext.getCurrentEntityClasses().values()) { |
| if (migrationContext.getCurrentDataStore().findAll(entity, null).size() > 0) { |
| return false; |
| } |
| } |
| } catch (PersistenceException e) { |
| ViewInstanceDefinition current = migrationContext.getCurrentInstanceDefinition(); |
| LOG.error("Persistence exception while check if instance is empty: " + |
| current.getViewDefinition().getViewName() + "{" + current.getViewDefinition().getVersion() + "}/" + |
| current.getInstanceName(), e); |
| } |
| |
| return true; |
| } |
| |
| /** |
| * Create the data migration context for DataMigrator to access data of current |
| * and origin instances. |
| * @param targetInstanceDefinition target instance definition |
| * @param sourceInstanceDefinition source instance definition |
| * @return data migration context |
| */ |
| protected ViewDataMigrationContextImpl getViewDataMigrationContext(ViewInstanceEntity targetInstanceDefinition, |
| ViewInstanceEntity sourceInstanceDefinition) { |
| return new ViewDataMigrationContextImpl(sourceInstanceDefinition, targetInstanceDefinition); |
| } |
| |
| /** |
| * Get the migrator instance for view instance with injected migration context. |
| * If versions of instances are same returns copy-all-data migrator. |
| * If versions are different, loads the migrator from the current view (view should |
| * contain ViewDataMigrator implementation, otherwise exception will be raised). |
| * |
| * @param currentInstanceDefinition the current view instance definition |
| * @param migrationContext the migration context to inject into migrator |
| * @throws ViewDataMigrationException if view does not support migration |
| * @return the data migration instance |
| */ |
| protected ViewDataMigrator getViewDataMigrator(ViewInstanceEntity currentInstanceDefinition, |
| ViewDataMigrationContextImpl migrationContext) |
| throws ViewDataMigrationException { |
| ViewDataMigrator dataMigrator; |
| |
| LOG.info("Migrating " + currentInstanceDefinition.getInstanceName() + |
| " data from " + migrationContext.getOriginDataVersion() + " to " + |
| migrationContext.getCurrentDataVersion() + " data version"); |
| |
| if (migrationContext.getOriginDataVersion() == migrationContext.getCurrentDataVersion()) { |
| |
| LOG.info("Instances of same version, copying all data."); |
| dataMigrator = new CopyAllDataMigrator(migrationContext); |
| } else { |
| try { |
| dataMigrator = currentInstanceDefinition.getDataMigrator(migrationContext); |
| if (dataMigrator == null) { |
| throw new ViewDataMigrationException("A view instance " + |
| currentInstanceDefinition.getInstanceName() + " does not support migration."); |
| } |
| LOG.debug("Data migrator loaded"); |
| } catch (ClassNotFoundException e) { |
| String msg = "Caught exception loading data migrator of " + currentInstanceDefinition.getInstanceName(); |
| |
| LOG.error(msg, e); |
| throw new RuntimeException(msg); |
| } |
| } |
| return dataMigrator; |
| } |
| |
| |
| /** |
| * The data migrator implementation that copies all data without modification. |
| * Used to copy data between instances of same version. |
| */ |
| public static class CopyAllDataMigrator implements ViewDataMigrator { |
| private ViewDataMigrationContext migrationContext; |
| |
| public CopyAllDataMigrator(ViewDataMigrationContext migrationContext) { |
| this.migrationContext = migrationContext; |
| } |
| |
| @Override |
| public boolean beforeMigration() { |
| return true; |
| } |
| |
| @Override |
| public void afterMigration() { |
| } |
| |
| @Override |
| public void migrateEntity(Class originEntityClass, Class currentEntityClass) |
| throws ViewDataMigrationException { |
| if (currentEntityClass == null) { |
| return; |
| } |
| migrationContext.copyAllObjects(originEntityClass, currentEntityClass); |
| } |
| |
| @Override |
| public void migrateInstanceData() { |
| migrationContext.copyAllInstanceData(); |
| } |
| } |
| } |