package com.thinkbiganalytics.metadata.upgrade.version_0_7_1;
/*-
* #%L
* kylo-operational-metadata-upgrade-service
* %%
* Copyright (C) 2017 ThinkBig Analytics
* %%
* 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.
* #L%
*/
import com.thinkbiganalytics.KyloVersion;
import com.thinkbiganalytics.metadata.modeshape.JcrMetadataAccess;
import com.thinkbiganalytics.metadata.modeshape.feed.JcrFeed;
import com.thinkbiganalytics.metadata.modeshape.support.JcrUtil;
import com.thinkbiganalytics.metadata.upgrade.UpgradeException;
import com.thinkbiganalytics.metadata.upgrade.UpgradeState;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;
import javax.annotation.Nonnull;
import javax.jcr.Node;
import javax.jcr.Property;
import javax.jcr.PropertyType;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
public class UpgradeAction implements UpgradeState {
private static final Logger log = LoggerFactory.getLogger(UpgradeAction.class);
private static final String CATEGORY_TYPE = "tba:category";
private static final String FEED_TYPE = "tba:feed";
private static final String UPGRADABLE_TYPE = "tba:upgradable";
private static final String CAT_DETAILS_TYPE = "tba:categoryDetails";
private static final String FEED_SUMMARY_TYPE = "tba:feedSummary";
private static final String FEED_DETAILS_TYPE = "tba:feedDetails";
private static final String FEED_DATA_TYPE = "tba:feedData";
/* (non-Javadoc)
* @see com.thinkbiganalytics.metadata.upgrade.UpgradeState#getStartingVersion()
*/
@Override
public KyloVersion getStartingVersion() {
return asVersion("0.7", "1");
}
/* (non-Javadoc)
* @see com.thinkbiganalytics.metadata.upgrade.UpgradeState#upgradeFrom(com.thinkbiganalytics.metadata.api.app.KyloVersion)
*/
@Override
public void upgradeFrom(KyloVersion startingVersion) {
log.info("Upgrading from version: " + startingVersion);
Session session = JcrMetadataAccess.getActiveSession();
Node feedsNode = JcrUtil.getNode(session, "metadata/feeds");
int categoryCount = 0;
int categoryFeedCount = 0;
int totalFeedCount = 0;
for (Node catNode : JcrUtil.getNodesOfType(feedsNode, CATEGORY_TYPE)) {
log.info("Starting upgrading category: [{}] {}", ++categoryCount, catNode);
categoryFeedCount = 0;
Node detailsNode = JcrUtil.getOrCreateNode(catNode, "tba:details", CAT_DETAILS_TYPE);
moveProperty("tba:initialized", catNode, detailsNode);
moveProperty("tba:securityGroups", catNode, detailsNode);
for (Node feedNode : JcrUtil.getNodesOfType(catNode, FEED_TYPE)) {
moveNode(session, feedNode, detailsNode);
log.info("\tStarting upgrading feed: [{}] {}", ++categoryFeedCount, feedNode);
++totalFeedCount;
Node feedSummaryNode = JcrUtil.getOrCreateNode(feedNode, "tba:summary", FEED_SUMMARY_TYPE);
Node feedDataNode = JcrUtil.getOrCreateNode(feedNode, "tba:data", FEED_DATA_TYPE);
addMixin(feedNode, UPGRADABLE_TYPE);
moveProperty(JcrFeed.SYSTEM_NAME, feedNode, feedSummaryNode);
moveProperty("tba:category", feedNode, feedSummaryNode);
moveProperty("tba:tags", feedNode, feedSummaryNode);
moveProperty(JcrFeed.TITLE, feedNode, feedSummaryNode);
moveProperty(JcrFeed.DESCRIPTION, feedNode, feedSummaryNode);
if (JcrUtil.hasNode(feedNode, "tba:properties")) {
final Node feedPropertiesNode = JcrUtil.getNode(feedNode, "tba:properties");
moveNode(session, feedPropertiesNode, feedSummaryNode);
}
Node feedDetailsNode = JcrUtil.getOrCreateNode(feedSummaryNode, "tba:details", FEED_DETAILS_TYPE);
moveProperty("tba:feedTemplate", feedNode, feedDetailsNode);
moveProperty("tba:slas", feedNode, feedDetailsNode);
moveProperty("tba:dependentFeeds", feedNode, feedDetailsNode, PropertyType.WEAKREFERENCE);
moveProperty("tba:usedByFeeds", feedNode, feedDetailsNode, PropertyType.WEAKREFERENCE);
moveProperty("tba:json", feedNode, feedDetailsNode);
// Loop is needed because sns is specified for node type
List<Node> feedSourceNodes = JcrUtil.getNodeList(feedNode, "tba:sources");
for (Node feedSourceNode : feedSourceNodes) {
moveNode(session, feedSourceNode, feedDetailsNode);
}
// Loop is needed because sns is specified for node type
List<Node> feedDestinationNodes = JcrUtil.getNodeList(feedNode, "tba:destinations");
for (Node feedDestinationNode : feedDestinationNodes) {
moveNode(session, feedDestinationNode, feedDetailsNode);
}
if (JcrUtil.hasNode(feedNode, "tba:precondition")) {
Node feedPreconditionNode = JcrUtil.getNode(feedNode, "tba:precondition");
moveNode(session, feedPreconditionNode, feedDetailsNode);
}
moveProperty("tba:state", feedNode, feedDataNode);
moveProperty("tba:schedulingPeriod", feedNode, feedDataNode);
moveProperty("tba:schedulingStrategy", feedNode, feedDataNode);
moveProperty("tba:securityGroups", feedNode, feedDataNode);
if (JcrUtil.hasNode(feedNode, "tba:highWaterMarks")) {
Node feedWaterMarksNode = JcrUtil.getNode(feedNode, "tba:highWaterMarks");
moveNode(session, feedWaterMarksNode, feedDataNode);
}
if (JcrUtil.hasNode(feedNode, "tba:initialization")) {
Node feedInitializationNode = JcrUtil.getNode(feedNode, "tba:initialization");
moveNode(session, feedInitializationNode, feedDataNode);
}
removeMixin(feedNode, UPGRADABLE_TYPE);
log.info("\tCompleted upgrading feed: " + feedNode);
}
log.info("Completed upgrading category: " + catNode);
}
// Update templates
int templateCount = 0;
final Node templatesNode = JcrUtil.getNode(session, "metadata/templates");
for (Node templateNode : JcrUtil.getNodesOfType(templatesNode, "tba:feedTemplate")) {
log.info("Starting upgrading template: [{}] {}", ++templateCount, templateNode);
JcrUtil.getOrCreateNode(templateNode, "tba:allowedActions", "tba:allowedActions");
log.info("Completed upgrading template: " + templateNode);
}
log.info("Upgrade complete for {} categories and {} feeds and {} templates", categoryCount, totalFeedCount, templateCount);
}
/**
* Adds the specified mixin to the specified node.
*
* @param node the target node
* @param mixinName the name of the mixin
*/
private void addMixin(@Nonnull final Node node, @Nonnull final String mixinName) {
try {
node.addMixin(mixinName);
} catch (final RepositoryException e) {
throw new UpgradeException("Failed to add mixin " + mixinName + " to node " + node, e);
}
}
private void moveNode(Session session, Node node, Node parentNode) {
try {
if ((node != null) && (parentNode != null)) {
final String srcPath = node.getParent().getPath() + "/" + StringUtils.substringAfterLast(node.getPath(), "/"); // Path may not be accurate if parent node moved recently
session.move(srcPath, parentNode.getPath() + "/" + node.getName());
}
} catch (RepositoryException e) {
throw new UpgradeException("Failed to moved node " + node + " under parent " + parentNode, e);
}
}
/**
* move a property from one node to another
*
* @param propName the name of the property to move
* @param fromNode the node to move from
* @param toNode the node to move to
* @param propertyType Optional. This is the new property type, or null if unchanged
*/
private void moveProperty(String propName, Node fromNode, Node toNode, Integer propertyType) {
try {
if ((fromNode != null) && (toNode != null)) {
if (fromNode.hasProperty(propName)) {
Property prop = fromNode.getProperty(propName);
if (propertyType == null) {
propertyType = prop.getType();
}
if (propertyType != prop.getType()) {
log.info("Property type for {} on Node {} is changing from {} to {} ", propName, fromNode.getName(), prop.getType(), propertyType);
}
if (prop.isMultiple()) {
toNode.setProperty(propName, prop.getValues(), propertyType);
} else {
toNode.setProperty(propName, prop.getValue(), propertyType);
}
prop.remove();
}
}
} catch (RepositoryException e) {
throw new UpgradeException("Failed to moved property " + propName + " from " + fromNode + " to " + toNode, e);
}
}
/**
* Move a property from one node to another keeping the same property type
*
* @param propName the name of the property to move
* @param fromNode the node to move the property from
* @param toNode the new node to move it to
*/
private void moveProperty(String propName, Node fromNode, Node toNode) {
moveProperty(propName, fromNode, toNode, null);
}
/**
* Removes the specified mixin from the specified node.
*
* @param node the target node
* @param mixinName the name of the mixin
*/
private void removeMixin(@Nonnull final Node node, @Nonnull final String mixinName) {
try {
node.removeMixin(mixinName);
} catch (final RepositoryException e) {
throw new UpgradeException("Failed to remove mixin " + mixinName + " from node " + node, e);
}
}
}