/**
* 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.falcon.oozie.process;
import org.apache.commons.lang3.StringUtils;
import org.apache.falcon.FalconException;
import org.apache.falcon.entity.EntityUtil;
import org.apache.falcon.entity.FeedHelper;
import org.apache.falcon.entity.Storage;
import org.apache.falcon.entity.store.ConfigurationStore;
import org.apache.falcon.entity.v0.EntityType;
import org.apache.falcon.entity.v0.cluster.Cluster;
import org.apache.falcon.entity.v0.feed.Feed;
import org.apache.falcon.entity.v0.feed.Location;
import org.apache.falcon.entity.v0.feed.LocationType;
import org.apache.falcon.entity.v0.process.Input;
import org.apache.falcon.entity.v0.process.Output;
import org.apache.falcon.entity.v0.process.Property;
import org.apache.falcon.expression.ExpressionHelper;
import org.apache.falcon.util.DateUtil;
import org.apache.falcon.workflow.WorkflowExecutionArgs;
import org.apache.hadoop.fs.Path;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.Properties;
/**
* Workflow Builder for oozie process in case of Native Scheduler.
*/
public class NativeOozieProcessWorkflowBuilder extends OozieProcessWorkflowBuilder {
private static final ExpressionHelper EXPRESSION_HELPER = ExpressionHelper.get();
private static final String INSTANCE_FORMAT = "yyyy-MM-dd-HH-mm";
public NativeOozieProcessWorkflowBuilder(org.apache.falcon.entity.v0.process.Process entity) {
super(entity);
}
@Override
public java.util.Properties build(Cluster cluster,
Path buildPath, Properties suppliedProps) throws FalconException {
Properties elProps = new Properties();
DateTimeFormatter fmt = DateTimeFormat.forPattern(INSTANCE_FORMAT);
elProps.put(WorkflowExecutionArgs.NOMINAL_TIME.getName(), fmt.print(getNominalTime()));
elProps.put(WorkflowExecutionArgs.TIMESTAMP.getName(), fmt.print(getNominalTime()));
elProps.put(WorkflowExecutionArgs.USER_JMS_NOTIFICATION_ENABLED.getName(), "true");
elProps.put(WorkflowExecutionArgs.SYSTEM_JMS_NOTIFICATION_ENABLED.getName(), "false"); //check true or false
DateUtil.setTimeZone(entity.getTimezone().getID());
ExpressionHelper.setReferenceDate(new Date(getNominalTime().getMillis()));
elProps.putAll(getInputProps(cluster));
elProps.putAll(getOutputProps());
elProps.putAll(evalProperties());
Properties buildProps = build(cluster, buildPath);
buildProps.putAll(elProps);
copyPropsWithoutOverride(buildProps, suppliedProps);
return buildProps;
}
private void copyPropsWithoutOverride(Properties buildProps, Properties suppliedProps) {
if (suppliedProps == null || suppliedProps.isEmpty()) {
return;
}
for (String propertyName : suppliedProps.stringPropertyNames()) {
if (buildProps.containsKey(propertyName)) {
LOG.warn("User provided property {} is already declared in the entity and will be ignored.",
propertyName);
continue;
}
String propertyValue = suppliedProps.getProperty(propertyName);
buildProps.put(propertyName, propertyValue);
}
}
private Properties evalProperties() throws FalconException {
Properties props = new Properties();
org.apache.falcon.entity.v0.process.Properties processProps = entity.getProperties();
for (Property property : processProps.getProperties()) {
String propName = property.getName();
String propValue = property.getValue();
String evalExp = EXPRESSION_HELPER.evaluateFullExpression(propValue, String.class);
props.put(propName, evalExp);
}
return props;
}
private Properties getOutputProps() throws FalconException {
Properties props = new Properties();
if (entity.getOutputs() == null) {
props.put(WorkflowExecutionArgs.OUTPUT_FEED_NAMES.getName(), NONE);
props.put(WorkflowExecutionArgs.OUTPUT_FEED_PATHS.getName(), NONE);
props.put(WorkflowExecutionArgs.OUTPUT_NAMES.getName(), NONE);
return props;
}
List<String> falconOutputFeeds = new LinkedList<>();
List<String> feedInstancePaths= new LinkedList<>();
List<String> falconOutputNames = new LinkedList<>();
for (Output output : entity.getOutputs().getOutputs()) {
Feed feed = ConfigurationStore.get().get(EntityType.FEED, output.getFeed());
falconOutputFeeds.add(feed.getName());
falconOutputNames.add(output.getName());
String outputExp = output.getInstance();
Date outTime = EXPRESSION_HELPER.evaluate(outputExp, Date.class);
for (org.apache.falcon.entity.v0.feed.Cluster cluster : feed.getClusters().getClusters()) {
org.apache.falcon.entity.v0.cluster.Cluster clusterEntity =
EntityUtil.getEntity(EntityType.CLUSTER, cluster.getName());
if (!EntityUtil.responsibleFor(clusterEntity.getColo())) {
continue;
}
List<Location> locations = FeedHelper.getLocations(cluster, feed);
for (Location loc : locations) {
String path = EntityUtil.evaluateDependentPath(loc.getPath(), outTime);
path = getStoragePath(path);
if (loc.getType() != LocationType.DATA) {
props.put(output.getName() + "." + loc.getType().toString().toLowerCase(), path);
} else {
props.put(output.getName(), path);
}
feedInstancePaths.add(path);
}
}
}
props.put(WorkflowExecutionArgs.OUTPUT_FEED_NAMES.getName(), StringUtils.join(falconOutputFeeds, ","));
props.put(WorkflowExecutionArgs.OUTPUT_NAMES.getName(), StringUtils.join(falconOutputNames, ","));
props.put(WorkflowExecutionArgs.OUTPUT_FEED_PATHS.getName(), StringUtils.join(feedInstancePaths, ","));
return props;
}
private Properties getInputProps(Cluster clusterObj) throws FalconException {
Properties props = new Properties();
if (entity.getInputs() == null) {
props.put(WorkflowExecutionArgs.INPUT_FEED_NAMES.getName(), NONE);
props.put(WorkflowExecutionArgs.INPUT_FEED_PATHS.getName(), NONE);
props.put(WorkflowExecutionArgs.INPUT_NAMES.getName(), NONE);
props.put(WorkflowExecutionArgs.INPUT_STORAGE_TYPES.getName(), NONE);
return props;
}
List<String> falconInputFeeds = new LinkedList<>();
List<String> falconInputNames = new LinkedList<>();
List<String> falconInputPaths = new LinkedList<>();
List<String> falconInputFeedStorageTypes = new LinkedList<>();
for (Input input : entity.getInputs().getInputs()) {
Feed feed = ConfigurationStore.get().get(EntityType.FEED, input.getFeed());
Storage storage = FeedHelper.createStorage(clusterObj, feed);
if (storage.getType() != Storage.TYPE.FILESYSTEM) {
throw new UnsupportedOperationException("Storage Type not supported " + storage.getType());
}
falconInputFeeds.add(feed.getName());
falconInputNames.add(input.getName());
falconInputFeedStorageTypes.add(storage.getType().name());
String partition = input.getPartition();
String startTimeExp = input.getStart();
String endTimeExp = input.getEnd();
ExpressionHelper.setReferenceDate(new Date(getNominalTime().getMillis()));
Date startTime = EXPRESSION_HELPER.evaluate(startTimeExp, Date.class);
Date endTime = EXPRESSION_HELPER.evaluate(endTimeExp, Date.class);
for (org.apache.falcon.entity.v0.feed.Cluster cluster : feed.getClusters().getClusters()) {
org.apache.falcon.entity.v0.cluster.Cluster clusterEntity =
EntityUtil.getEntity(EntityType.CLUSTER, cluster.getName());
if (!EntityUtil.responsibleFor(clusterEntity.getColo())) {
continue;
}
List<Location> locations = FeedHelper.getLocations(cluster, feed);
for (Location loc : locations) {
if (loc.getType() != LocationType.DATA) {
continue;
}
List<String> paths = new ArrayList<>();
List<Date> instanceTimes = EntityUtil.getEntityInstanceTimes(feed, cluster.getName(),
startTime, endTime); // test when startTime and endTime are equal.
for (Date instanceTime : instanceTimes) {
String path = EntityUtil.evaluateDependentPath(loc.getPath(), instanceTime);
if (StringUtils.isNotBlank(partition)) {
if (!path.endsWith("/") && !partition.startsWith("/")) {
path = path + "/";
}
path = path + partition;
}
path = getStoragePath(path);
paths.add(path);
}
if (loc.getType() != LocationType.DATA) {
props.put(input.getName() + "." + loc.getType().toString().toLowerCase(),
StringUtils.join(paths, ","));
} else {
props.put(input.getName(), StringUtils.join(paths, ","));
}
falconInputPaths.add(StringUtils.join(paths, ","));
}
}
}
props.put(WorkflowExecutionArgs.INPUT_FEED_NAMES.getName(), StringUtils.join(falconInputFeeds, "#"));
props.put(WorkflowExecutionArgs.INPUT_NAMES.getName(), StringUtils.join(falconInputNames, "#"));
props.put(WorkflowExecutionArgs.INPUT_FEED_PATHS.getName(), StringUtils.join(falconInputPaths, "#"));
props.put(WorkflowExecutionArgs.INPUT_STORAGE_TYPES.getName(),
StringUtils.join(falconInputFeedStorageTypes, "#"));
return props;
}
}