All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.datatorrent.stram.client.AppPackage Maven / Gradle / Ivy

There is a newer version: 3.7.0
Show newest version
/**
 * 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 com.datatorrent.stram.client;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.jar.Attributes;
import java.util.jar.JarFile;
import java.util.jar.Manifest;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.exception.ExceptionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.hadoop.conf.Configuration;

import com.datatorrent.stram.client.StramAppLauncher.AppFactory;
import com.datatorrent.stram.plan.logical.LogicalPlan;

import net.lingala.zip4j.core.ZipFile;
import net.lingala.zip4j.exception.ZipException;
import net.lingala.zip4j.model.ZipParameters;


/**
 * 

* AppPackage class.

* * @since 1.0.3 */ public class AppPackage extends JarFile { public static final String ATTRIBUTE_DT_ENGINE_VERSION = "DT-Engine-Version"; public static final String ATTRIBUTE_DT_APP_PACKAGE_NAME = "DT-App-Package-Name"; public static final String ATTRIBUTE_DT_APP_PACKAGE_VERSION = "DT-App-Package-Version"; public static final String ATTRIBUTE_DT_APP_PACKAGE_GROUP_ID = "DT-App-Package-Group-Id"; public static final String ATTRIBUTE_CLASS_PATH = "Class-Path"; public static final String ATTRIBUTE_DT_APP_PACKAGE_DISPLAY_NAME = "DT-App-Package-Display-Name"; public static final String ATTRIBUTE_DT_APP_PACKAGE_DESCRIPTION = "DT-App-Package-Description"; private final String appPackageName; private final String appPackageVersion; private final String appPackageGroupId; private final String dtEngineVersion; private final String appPackageDescription; private final String appPackageDisplayName; private final ArrayList classPath = new ArrayList<>(); private final File directory; private final List applications = new ArrayList<>(); private final List appJars = new ArrayList<>(); private final List appJsonFiles = new ArrayList<>(); private final List appPropertiesFiles = new ArrayList<>(); private final Set requiredProperties = new TreeSet<>(); private final Map defaultProperties = new TreeMap<>(); private final Set configs = new TreeSet<>(); private final File resourcesDirectory; private final boolean cleanOnClose; public static class AppInfo { public final String name; public final String file; public final String type; public String displayName; public LogicalPlan dag; public String error; public String errorStackTrace; public Set requiredProperties = new TreeSet<>(); public Map defaultProperties = new TreeMap<>(); public AppInfo(String name, String file, String type) { this.name = name; this.file = file; this.type = type; } } public AppPackage(File file) throws IOException, ZipException { this(file, false); } /** * Creates an App Package object. * * If app directory is to be processed, there may be resource leak in the class loader. Only pass true for short-lived * applications * * If contentFolder is not null, it will try to create the contentFolder, file will be retained on disk after App Package is closed * If contentFolder is null, temp folder will be created and will be cleaned on close() * * @param file * @param contentFolder the folder that the app package will be extracted to * @param processAppDirectory * @throws java.io.IOException * @throws net.lingala.zip4j.exception.ZipException */ public AppPackage(File file, File contentFolder, boolean processAppDirectory) throws IOException, ZipException { super(file); if (contentFolder != null) { FileUtils.forceMkdir(contentFolder); cleanOnClose = false; } else { cleanOnClose = true; contentFolder = Files.createTempDirectory("dt-appPackage-").toFile(); } directory = contentFolder; Manifest manifest = getManifest(); if (manifest == null) { throw new IOException("Not a valid app package. MANIFEST.MF is not present."); } Attributes attr = manifest.getMainAttributes(); appPackageName = attr.getValue(ATTRIBUTE_DT_APP_PACKAGE_NAME); appPackageVersion = attr.getValue(ATTRIBUTE_DT_APP_PACKAGE_VERSION); appPackageGroupId = attr.getValue(ATTRIBUTE_DT_APP_PACKAGE_GROUP_ID); dtEngineVersion = attr.getValue(ATTRIBUTE_DT_ENGINE_VERSION); appPackageDisplayName = attr.getValue(ATTRIBUTE_DT_APP_PACKAGE_DISPLAY_NAME); appPackageDescription = attr.getValue(ATTRIBUTE_DT_APP_PACKAGE_DESCRIPTION); String classPathString = attr.getValue(ATTRIBUTE_CLASS_PATH); if (appPackageName == null || appPackageVersion == null || classPathString == null) { throw new IOException("Not a valid app package. App Package Name or Version or Class-Path is missing from MANIFEST.MF"); } classPath.addAll(Arrays.asList(StringUtils.split(classPathString, " "))); extractToDirectory(directory, file); File confDirectory = new File(directory, "conf"); if (confDirectory.exists()) { processConfDirectory(confDirectory); } resourcesDirectory = new File(directory, "resources"); File propertiesXml = new File(directory, "META-INF/properties.xml"); if (propertiesXml.exists()) { processPropertiesXml(propertiesXml, null); } if (processAppDirectory) { processAppDirectory(false); } } private void processAppProperties() { for (AppInfo app : applications) { app.requiredProperties.addAll(requiredProperties); app.defaultProperties.putAll(defaultProperties); File appPropertiesXml = new File(directory, "META-INF/properties-" + app.name + ".xml"); if (appPropertiesXml.exists()) { processPropertiesXml(appPropertiesXml, app); } } } /** * Creates an App Package object. * * If app directory is to be processed, there may be resource leak in the class loader. Only pass true for short-lived * applications * * Files in app package will be extracted to tmp folder and will be cleaned on close() * The close() method could be explicitly called or implicitly called by GC finalize() * * @param file * @param processAppDirectory * @throws java.io.IOException * @throws net.lingala.zip4j.exception.ZipException */ public AppPackage(File file, boolean processAppDirectory) throws IOException, ZipException { this(file, null, processAppDirectory); } public static void extractToDirectory(File directory, File appPackageFile) throws ZipException { ZipFile zipFile = new ZipFile(appPackageFile); if (zipFile.isEncrypted()) { throw new ZipException("Encrypted app package not supported yet"); } directory.mkdirs(); zipFile.extractAll(directory.getAbsolutePath()); } public static void createAppPackageFile(File fileToBeCreated, File directory) throws ZipException { ZipFile zipFile = new ZipFile(fileToBeCreated); ZipParameters params = new ZipParameters(); params.setIncludeRootFolder(false); zipFile.addFolder(directory, params); } public File tempDirectory() { return directory; } @Override public void close() throws IOException { super.close(); if (cleanOnClose) { cleanContent(); } } public void cleanContent() throws IOException { FileUtils.deleteDirectory(directory); LOG.debug("App Package {}-{} folder {} is removed", appPackageName, appPackageVersion, directory.getAbsolutePath()); } public String getAppPackageName() { return appPackageName; } public String getAppPackageVersion() { return appPackageVersion; } public String getAppPackageGroupId() { return appPackageGroupId; } public String getAppPackageDescription() { return appPackageDescription; } public String getAppPackageDisplayName() { return appPackageDisplayName; } public String getDtEngineVersion() { return dtEngineVersion; } public List getClassPath() { return Collections.unmodifiableList(classPath); } public Collection getConfigs() { return Collections.unmodifiableCollection(configs); } public File resourcesDirectory() { return resourcesDirectory; } public List getApplications() { return Collections.unmodifiableList(applications); } public List getAppJars() { return Collections.unmodifiableList(appJars); } public List getAppJsonFiles() { return Collections.unmodifiableList(appJsonFiles); } public List getAppPropertiesFiles() { return Collections.unmodifiableList(appPropertiesFiles); } public Set getRequiredProperties() { return Collections.unmodifiableSet(requiredProperties); } public Map getDefaultProperties() { return Collections.unmodifiableMap(defaultProperties); } public void processAppDirectory(boolean skipJars) { File dir = new File(directory, "app"); applications.clear(); Configuration config = new Configuration(); List absClassPath = new ArrayList<>(classPath); for (int i = 0; i < absClassPath.size(); i++) { String path = absClassPath.get(i); if (!path.startsWith("/")) { absClassPath.set(i, directory + "/" + path); } } config.set(StramAppLauncher.LIBJARS_CONF_KEY_NAME, StringUtils.join(absClassPath, ',')); File[] files = dir.listFiles(); for (File entry : files) { if (entry.getName().endsWith(".jar") && !skipJars) { appJars.add(entry.getName()); try { StramAppLauncher stramAppLauncher = new StramAppLauncher(entry, config); stramAppLauncher.loadDependencies(); List appFactories = stramAppLauncher.getBundledTopologies(); for (AppFactory appFactory : appFactories) { String appName = stramAppLauncher.getLogicalPlanConfiguration().getAppAlias(appFactory.getName()); if (appName == null) { appName = appFactory.getName(); } AppInfo appInfo = new AppInfo(appName, entry.getName(), "class"); appInfo.displayName = appFactory.getDisplayName(); try { appInfo.dag = appFactory.createApp(stramAppLauncher.getLogicalPlanConfiguration()); appInfo.dag.validate(); } catch (Throwable ex) { appInfo.error = ex.getMessage(); appInfo.errorStackTrace = ExceptionUtils.getStackTrace(ex); } applications.add(appInfo); } } catch (Exception ex) { LOG.error("Caught exception trying to process {}", entry.getName(), ex); } } } // this is for the properties and json files to be able to depend on the app jars, // since it's possible for users to implement the operators as part of the app package for (String appJar : appJars) { absClassPath.add(new File(dir, appJar).getAbsolutePath()); } config.set(StramAppLauncher.LIBJARS_CONF_KEY_NAME, StringUtils.join(absClassPath, ',')); files = dir.listFiles(); for (File entry : files) { if (entry.getName().endsWith(".json")) { appJsonFiles.add(entry.getName()); AppInfo appInfo = StramClientUtils.jsonFileToAppInfo(entry, config); if (appInfo != null) { applications.add(appInfo); } } else if (entry.getName().endsWith(".properties")) { appPropertiesFiles.add(entry.getName()); try { AppFactory appFactory = new StramAppLauncher.PropertyFileAppFactory(entry); StramAppLauncher stramAppLauncher = new StramAppLauncher(entry.getName(), config); stramAppLauncher.loadDependencies(); AppInfo appInfo = new AppInfo(appFactory.getName(), entry.getName(), "properties"); appInfo.displayName = appFactory.getDisplayName(); try { appInfo.dag = appFactory.createApp(stramAppLauncher.getLogicalPlanConfiguration()); appInfo.dag.validate(); } catch (Throwable t) { appInfo.error = t.getMessage(); appInfo.errorStackTrace = ExceptionUtils.getStackTrace(t); } applications.add(appInfo); } catch (Exception ex) { LOG.error("Caught exceptions trying to process {}", entry.getName(), ex); } } else if (!entry.getName().endsWith(".jar")) { LOG.warn("Ignoring file {} with unknown extension in app directory", entry.getName()); } } processAppProperties(); } private void processConfDirectory(File dir) { File[] files = dir.listFiles(); for (File entry : files) { if (entry.getName().endsWith(".xml")) { configs.add(entry.getName()); } } } private void processPropertiesXml(File file, AppInfo app) { DTConfiguration config = new DTConfiguration(); try { config.loadFile(file); for (Map.Entry entry : config) { String key = entry.getKey(); String value = entry.getValue(); if (value == null) { if (app == null) { requiredProperties.add(key); } else { app.requiredProperties.add(key); } } else { if (app == null) { defaultProperties.put(key, value); } else { app.requiredProperties.remove(key); app.defaultProperties.put(key, value); } } } } catch (Exception ex) { LOG.warn("Ignoring META_INF/properties.xml because of error", ex); } } private static final Logger LOG = LoggerFactory.getLogger(AppPackage.class); }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy