/** * 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 java.util.HashMap; import java.util.Map; import java.util.WeakHashMap; import org.apache.ambari.server.orm.entities.ViewEntity; import org.apache.ambari.server.orm.entities.ViewInstanceDataEntity; import org.apache.ambari.server.orm.entities.ViewInstanceEntity; import org.apache.ambari.server.view.configuration.EntityConfig; import org.apache.ambari.server.view.configuration.PersistenceConfig; import org.apache.ambari.server.view.persistence.DataStoreImpl; import org.apache.ambari.server.view.persistence.DataStoreModule; import org.apache.ambari.view.DataStore; import org.apache.ambari.view.PersistenceException; import org.apache.ambari.view.migration.EntityConverter; import org.apache.ambari.view.migration.ViewDataMigrationContext; import org.apache.ambari.view.migration.ViewDataMigrationException; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.beans.BeanUtils; import com.google.inject.Guice; import com.google.inject.Injector; import com.google.inject.persist.Transactional; /** * View data migration context implementation. */ public class ViewDataMigrationContextImpl implements ViewDataMigrationContext { /** * Logger. */ private static final Log LOG = LogFactory.getLog(ViewDataMigrationContextImpl.class); /** * The data store of origin(source) view instance with source data. */ private DataStore originDataStore; /** * The data store of current(target) view instance to store data. */ private DataStore currentDataStore; /** * The origin view instance definition. */ private final ViewInstanceEntity originInstanceDefinition; /** * The current view instance definition. */ private final ViewInstanceEntity currentInstanceDefinition; /** * Constructor. * * @param originInstanceDefinition the origin view instance definition * @param currentInstanceDefinition the current view instance definition */ public ViewDataMigrationContextImpl(ViewInstanceEntity originInstanceDefinition, ViewInstanceEntity currentInstanceDefinition) { this.originInstanceDefinition = originInstanceDefinition; this.currentInstanceDefinition = currentInstanceDefinition; } private Map<ViewInstanceEntity, DataStoreModule> dataStoreModules = new WeakHashMap<>(); /** * Instantiates the data store associated with the instance. * * @param instanceDefinition the view instance definition * @return the data store object associated with view instance */ protected DataStore getDataStore(ViewInstanceEntity instanceDefinition) { if (!dataStoreModules.containsKey(instanceDefinition)) { DataStoreModule module = new DataStoreModule(instanceDefinition,"ambari-view-migration"); dataStoreModules.put(instanceDefinition, module); } Injector injector = Guice.createInjector(dataStoreModules.get(instanceDefinition)); return injector.getInstance(DataStoreImpl.class); } @Override public int getCurrentDataVersion() { return currentInstanceDefinition.getViewEntity().getConfiguration().getDataVersion(); } @Override public int getOriginDataVersion() { return originInstanceDefinition.getViewEntity().getConfiguration().getDataVersion(); } @Override public DataStore getOriginDataStore() { if (originDataStore == null) { originDataStore = getDataStore(originInstanceDefinition); } return originDataStore; } @Override public DataStore getCurrentDataStore() { if (currentDataStore == null) { currentDataStore = getDataStore(currentInstanceDefinition); } return currentDataStore; } @Override @Transactional public void putCurrentInstanceData(String user, String key, String value) { putInstanceData(currentInstanceDefinition, user, key, value); } @Override public void copyAllObjects(Class originEntityClass, Class currentEntityClass) throws ViewDataMigrationException { copyAllObjects(originEntityClass, currentEntityClass, new EntityConverter() { @Override public void convert(Object orig, Object dest) { BeanUtils.copyProperties(orig, dest); } }); } @Override @Transactional public void copyAllObjects(Class originEntityClass, Class currentEntityClass, EntityConverter entityConverter) throws ViewDataMigrationException { try{ for (Object origInstance : getOriginDataStore().findAll(originEntityClass, null)) { Object newInstance = currentEntityClass.newInstance(); entityConverter.convert(origInstance, newInstance); getCurrentDataStore().store(newInstance); } } catch (PersistenceException | InstantiationException | IllegalAccessException e) { String msg = "Error occured during copying data. Persistence entities are not compatible."; LOG.error(msg); throw new ViewDataMigrationException(msg, e); } } @Override public void copyAllInstanceData() { for (Map.Entry<String, Map<String, String>> userData : getOriginInstanceDataByUser().entrySet()) { for (Map.Entry<String, String> entry : userData.getValue().entrySet()) { putCurrentInstanceData(userData.getKey(), entry.getKey(), entry.getValue()); } } } @Override public ViewInstanceEntity getOriginInstanceDefinition() { return originInstanceDefinition; } @Override public Map<String, Class> getOriginEntityClasses() { ViewEntity viewDefinition = originInstanceDefinition.getViewEntity(); return getPersistenceClassesOfView(viewDefinition); } @Override public Map<String, Class> getCurrentEntityClasses() { ViewEntity viewDefinition = currentInstanceDefinition.getViewEntity(); return getPersistenceClassesOfView(viewDefinition); } /** * Get persistence entities of the view instance. * * @param viewDefinition the view definition. * @return the mapping of entity class name to the class objects, * loaded by the classloader of view version. */ private static Map<String, Class> getPersistenceClassesOfView(ViewEntity viewDefinition) { PersistenceConfig persistence = viewDefinition.getConfiguration().getPersistence(); HashMap<String, Class> classes = new HashMap<>(); if (persistence != null) { for (EntityConfig c : persistence.getEntities()) { try { Class entity = viewDefinition.getClassLoader().loadClass(c.getClassName()); classes.put(c.getClassName(), entity); } catch (ClassNotFoundException e) { e.printStackTrace(); } } } return classes; } @Override public ViewInstanceEntity getCurrentInstanceDefinition() { return currentInstanceDefinition; } @Override public Map<String, Map<String, String>> getOriginInstanceDataByUser() { return getInstanceDataByUser(originInstanceDefinition); } @Override @Transactional public void putOriginInstanceData(String user, String key, String value) { putInstanceData(originInstanceDefinition, user, key, value); } @Override public Map<String, Map<String, String>> getCurrentInstanceDataByUser() { return getInstanceDataByUser(currentInstanceDefinition); } public void closeMigration() { for (DataStoreModule module : dataStoreModules.values()) { module.close(); } dataStoreModules.clear(); } /** * Save an instance data value for the given key owned by given user. * * @param instanceDefinition the view instance definition * @param user the owner of the data value * @param name the name * @param value the value */ private static void putInstanceData(ViewInstanceEntity instanceDefinition, String user, String name, String value) { ViewInstanceDataEntity oldInstanceDataEntity = getInstanceData(instanceDefinition, user, name); if (oldInstanceDataEntity != null) { instanceDefinition.getData().remove(oldInstanceDataEntity); } ViewInstanceDataEntity instanceDataEntity = new ViewInstanceDataEntity(); instanceDataEntity.setViewName(instanceDefinition.getViewName()); instanceDataEntity.setViewInstanceName(instanceDefinition.getName()); instanceDataEntity.setName(name); instanceDataEntity.setUser(user); instanceDataEntity.setValue(value); instanceDataEntity.setViewInstanceEntity(instanceDefinition); instanceDefinition.getData().add(instanceDataEntity); } /** * Get the instance data entity for the given key and user. * * @param user owner of the data * @param key the key * @return the instance data entity associated with the given key and user */ private static ViewInstanceDataEntity getInstanceData(ViewInstanceEntity instanceDefinition, String user, String key) { for (ViewInstanceDataEntity viewInstanceDataEntity : instanceDefinition.getData()) { if (viewInstanceDataEntity.getName().equals(key) && viewInstanceDataEntity.getUser().equals(user)) { return viewInstanceDataEntity; } } return null; } /** * Get the instance data in the mapping of user owning data to the key-value data. * * @param instanceDefinition the view instance definition * @return mapping of the data owner to the instance data entries */ private static Map<String, Map<String, String>> getInstanceDataByUser(ViewInstanceEntity instanceDefinition) { Map<String, Map<String, String>> instanceDataByUser = new HashMap<>(); for (ViewInstanceDataEntity entity : instanceDefinition.getData()) { if (!instanceDataByUser.containsKey(entity.getUser())) { instanceDataByUser.put(entity.getUser(), new HashMap<String, String>()); } instanceDataByUser.get(entity.getUser()).put(entity.getName(), entity.getValue()); } return instanceDataByUser; } }