blob: 2906100514df745f680d772554228a33bffcd80a [file] [log] [blame]
/**
* 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();
}
}
}