/** * 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; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; import org.apache.falcon.FalconException; import org.apache.falcon.entity.CatalogStorage; import org.apache.falcon.entity.ClusterHelper; import org.apache.falcon.entity.EntityUtil; import org.apache.falcon.entity.HiveUtil; import org.apache.falcon.entity.v0.Entity; import org.apache.falcon.entity.v0.cluster.Cluster; import org.apache.falcon.entity.v0.cluster.ClusterLocationType; import org.apache.falcon.entity.v0.cluster.Interfacetype; import org.apache.falcon.entity.v0.feed.Feed; import org.apache.falcon.entity.v0.process.Output; import org.apache.falcon.entity.v0.process.Process; import org.apache.falcon.hadoop.HadoopClientFactory; import org.apache.falcon.oozie.feed.FeedBundleBuilder; import org.apache.falcon.oozie.process.ProcessBundleBuilder; import org.apache.falcon.security.SecurityUtil; import org.apache.falcon.service.FalconPathFilter; import org.apache.falcon.service.SharedLibraryHostingService; import org.apache.falcon.util.StartupProperties; import org.apache.falcon.workflow.engine.AbstractWorkflowEngine; import org.apache.falcon.workflow.util.OozieConstants; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.FileSystem; import org.apache.hadoop.fs.Path; import org.apache.oozie.client.OozieClient; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBElement; import javax.xml.bind.JAXBException; import javax.xml.bind.Marshaller; import javax.xml.bind.Unmarshaller; import javax.xml.transform.stream.StreamSource; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.StringWriter; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Properties; /** * Base class for building oozie entities - workflow, coordinator and bundle. * @param <T> */ public abstract class OozieEntityBuilder<T extends Entity> { public static final Logger LOG = LoggerFactory.getLogger(OozieEntityBuilder.class); public static final String ENTITY_PATH = "ENTITY_PATH"; public static final String ENTITY_NAME = "ENTITY_NAME"; // Used when the parameter exists but is not applicable for a particular action/scenario protected static final String IGNORE = "IGNORE"; // Used when the parameter is not available protected static final String NONE = "NONE"; private static final FalconPathFilter FALCON_JAR_FILTER = new FalconPathFilter() { @Override public boolean accept(Path path) { String fileName = path.getName(); if (fileName.startsWith("falcon")) { return true; } return false; } @Override public String getJarName(Path path) { String name = path.getName(); if (name.endsWith(".jar")) { name = name.substring(0, name.indexOf(".jar")); } return name; } }; protected T entity; protected final boolean isSecurityEnabled = SecurityUtil.isSecurityEnabled(); public OozieEntityBuilder(T entity) { this.entity = entity; } public abstract Properties build(Cluster cluster, Path buildPath) throws FalconException; public Properties build(Cluster cluster, Path buildPath, Map<String, String> properties) throws FalconException { Properties props = new Properties(); if (properties != null) { props.putAll(properties); } return build(cluster, buildPath, props); } public Properties build(Cluster cluster, Path buildPath, Properties properties) throws FalconException { Properties builderProperties = build(cluster, buildPath); if (properties == null || properties.isEmpty()) { return builderProperties; } Properties propertiesCopy = new Properties(); propertiesCopy.putAll(properties); // Builder properties shadow any user-defined property for(String propertyName : builderProperties.stringPropertyNames()) { String propertyValue = builderProperties.getProperty(propertyName); if (propertiesCopy.contains(propertyName)) { LOG.warn("User provided property {} is already declared in the entity and will be ignored.", propertyName); } propertiesCopy.put(propertyName, propertyValue); } return propertiesCopy; } protected String getStoragePath(Path path) { if (path != null) { return getStoragePath(path.toString()); } return null; } protected String getStoragePath(String path) { if (StringUtils.isNotEmpty(path)) { if (new Path(path).toUri().getScheme() == null && !path.startsWith("${nameNode}")) { path = "${nameNode}" + path; } } return path; } public static OozieEntityBuilder get(Entity entity) { switch (entity.getEntityType()) { case FEED: return new FeedBundleBuilder((Feed) entity); case PROCESS: return new ProcessBundleBuilder((Process) entity); default: } throw new IllegalArgumentException("Unhandled type: " + entity.getEntityType()); } protected Path marshal(Cluster cluster, JAXBElement<?> jaxbElement, JAXBContext jaxbContext, Path outPath) throws FalconException { try { Marshaller marshaller = jaxbContext.createMarshaller(); marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE); if (LOG.isDebugEnabled()) { StringWriter writer = new StringWriter(); marshaller.marshal(jaxbElement, writer); LOG.debug("Writing definition to {} on cluster {}", outPath, cluster.getName()); LOG.debug(writer.getBuffer().toString()); } FileSystem fs = HadoopClientFactory.get().createProxiedFileSystem( outPath.toUri(), ClusterHelper.getConfiguration(cluster)); OutputStream out = fs.create(outPath); try { marshaller.marshal(jaxbElement, out); } finally { out.close(); } LOG.info("Marshalled {} to {}", jaxbElement.getDeclaredType(), outPath); return outPath; } catch (Exception e) { throw new FalconException("Unable to marshall app object", e); } } protected Properties createAppProperties(Cluster cluster) throws FalconException { Properties properties = EntityUtil.getEntityProperties(cluster); properties.setProperty(AbstractWorkflowEngine.NAME_NODE, ClusterHelper.getStorageUrl(cluster)); properties.setProperty(AbstractWorkflowEngine.JOB_TRACKER, ClusterHelper.getMREndPoint(cluster)); properties.setProperty("colo.name", cluster.getColo()); final String endpoint = ClusterHelper.getInterface(cluster, Interfacetype.WORKFLOW).getEndpoint(); if (!OozieConstants.LOCAL_OOZIE.equals(endpoint)) { properties.setProperty(OozieClient.USE_SYSTEM_LIBPATH, "true"); } properties.setProperty("falcon.libpath", ClusterHelper.getLocation(cluster, ClusterLocationType.WORKING).getPath() + "/lib"); return properties; } protected Configuration getHiveCredentialsAsConf(Cluster cluster) { Properties hiveCredentials = HiveUtil.getHiveCredentials(cluster); Configuration hiveConf = new Configuration(false); for (Entry<Object, Object> entry : hiveCredentials.entrySet()) { hiveConf.set((String)entry.getKey(), (String)entry.getValue()); } return hiveConf; } protected void propagateCatalogTableProperties(Output output, CatalogStorage tableStorage, Properties props) { String prefix = "falcon_" + output.getName(); propagateCommonCatalogTableProperties(tableStorage, props, prefix); //pig and java actions require partition expression as "key1=val1, key2=val2" props.put(prefix + "_partitions_pig", "${coord:dataOutPartitions('" + output.getName() + "')}"); props.put(prefix + "_partitions_java", "${coord:dataOutPartitions('" + output.getName() + "')}"); //hive requires partition expression as "key1='val1', key2='val2'" (with quotes around values) //there is no direct EL expression in oozie List<String> partitions = new ArrayList<String>(); for (String key : tableStorage.getDatedPartitionKeys()) { StringBuilder expr = new StringBuilder(); expr.append("${coord:dataOutPartitionValue('").append(output.getName()).append("', '").append(key) .append("')}"); props.put(prefix + "_dated_partition_value_" + key, expr.toString()); partitions.add(key + "='" + expr + "'"); } props.put(prefix + "_partitions_hive", StringUtils.join(partitions, ",")); } protected void propagateCommonCatalogTableProperties(CatalogStorage tableStorage, Properties props, String prefix) { props.put(prefix + "_storage_type", tableStorage.getType().name()); props.put(prefix + "_catalog_url", tableStorage.getCatalogUrl()); props.put(prefix + "_database", tableStorage.getDatabase()); props.put(prefix + "_table", tableStorage.getTable()); } protected void copySharedLibs(Cluster cluster, Path libPath) throws FalconException { try { FileSystem fs = HadoopClientFactory.get().createProxiedFileSystem( libPath.toUri(), ClusterHelper.getConfiguration(cluster)); SharedLibraryHostingService.pushLibsToHDFS( fs, StartupProperties.get().getProperty("system.lib.location"), libPath, FALCON_JAR_FILTER); } catch (IOException e) { throw new FalconException("Failed to copy shared libs on cluster " + cluster.getName(), e); } } protected Properties getProperties(Path path, String name) { if (path == null) { return null; } Properties prop = new Properties(); prop.setProperty(OozieEntityBuilder.ENTITY_PATH, path.toString()); prop.setProperty(OozieEntityBuilder.ENTITY_NAME, name); return prop; } protected <T> T unmarshal(String template, JAXBContext context, Class<T> cls) throws FalconException { InputStream resourceAsStream = null; try { resourceAsStream = OozieEntityBuilder.class.getResourceAsStream(template); Unmarshaller unmarshaller = context.createUnmarshaller(); JAXBElement<T> jaxbElement = unmarshaller.unmarshal(new StreamSource(resourceAsStream), cls); return jaxbElement.getValue(); } catch (JAXBException e) { throw new FalconException("Failed to unmarshal " + template, e); } finally { IOUtils.closeQuietly(resourceAsStream); } } }