diff --git a/core/src/main/java/oracle/weblogic/deploy/util/WLSDeployArchive.java b/core/src/main/java/oracle/weblogic/deploy/util/WLSDeployArchive.java index 5facde7029..aca36b397f 100644 --- a/core/src/main/java/oracle/weblogic/deploy/util/WLSDeployArchive.java +++ b/core/src/main/java/oracle/weblogic/deploy/util/WLSDeployArchive.java @@ -58,6 +58,12 @@ public class WLSDeployArchive { */ public static final String ARCHIVE_APPS_TARGET_DIR = WLSDPLY_ARCHIVE_BINARY_DIR + "/applications"; + /** + * Top-level archive subdirectory where the applications are stored and the subdirectory to which + * they will be extracted. This is for structured applications found under /app + */ + public static final String ARCHIVE_STRUCT_APPS_TARGET_DIR = WLSDPLY_ARCHIVE_BINARY_DIR + "/structuredApplications"; + /** * Top-level archive subdirectory where the shared libraries are stored and the subdirectory to * which they will be extracted. @@ -573,6 +579,17 @@ public String getApplicationArchivePath(String appPath) { return getArchiveName(ARCHIVE_APPS_TARGET_DIR, appPath); } + /** + * Get the archive path for the application in a well-formed application directory + * @param appPath name of the application path + * @return archive path for use in the model + */ + public String getApplicationDirectoryArchivePath(String appName, String appPath) { + File zipAppPath = new File(appPath).getParentFile(); + File zipAppFile = new File(appPath); + return ARCHIVE_STRUCT_APPS_TARGET_DIR + "/" + appName + "/" + zipAppPath.getName() + "/" + zipAppFile.getName(); + } + /** * This method adds an application to the archive. If an application with the same name already exists, this * method assumes that the new one also needs to be added so it changes the name to prevent conflicts by adding @@ -590,6 +607,7 @@ public String addApplication(String appPath) throws WLSDeployArchiveIOException LOGGER.entering(CLASS, METHOD, appPath); File filePath = new File(appPath); + validateExistingFile(filePath, "appPath", getArchiveFileName(), METHOD, true); String newName = addItemToZip(ARCHIVE_APPS_TARGET_DIR, filePath); @@ -597,6 +615,32 @@ public String addApplication(String appPath) throws WLSDeployArchiveIOException return newName; } + public String addApplicationFolder(String appName, String appPath) + throws WLSDeployArchiveIOException { + final String METHOD = "addApplicationFolder"; + LOGGER.entering(CLASS, METHOD, appName, appPath); + File zipPath = new File(appPath); + if (zipPath.getParentFile() != null) { + zipPath = zipPath.getParentFile(); + } + String firstPrefix = ARCHIVE_STRUCT_APPS_TARGET_DIR + "/" + appName + "/" + zipPath.getName(); + String newName = walkDownFolders(firstPrefix, zipPath); + LOGGER.exiting(CLASS, METHOD, newName); + return newName; + } + + public String addApplicationPlanFolder(String appName, String planDir) + throws WLSDeployArchiveIOException { + final String METHOD = "addApplicationPathFolder"; + LOGGER.entering(CLASS, METHOD, appName, planDir); + File zipPlan = new File(planDir); + String zipPrefix = ARCHIVE_STRUCT_APPS_TARGET_DIR + "/" + appName + "/" + zipPlan.getName(); + String newName = walkDownFolders(zipPrefix, zipPlan); + + LOGGER.exiting(CLASS, METHOD, newName); + return zipPrefix; + } + /** * Get the list of application names in the archive. * @@ -671,11 +715,13 @@ public void extractApplication(String applicationPath, File domainHome) throws W validateExistingDirectory(domainHome, "domainHome", getArchiveFileName(), METHOD); String appPath = applicationPath; - if (!applicationPath.startsWith(ARCHIVE_APPS_TARGET_DIR)) { + if (!applicationPath.startsWith(ARCHIVE_STRUCT_APPS_TARGET_DIR) && + !applicationPath.startsWith(ARCHIVE_APPS_TARGET_DIR)) { appPath = ARCHIVE_APPS_TARGET_DIR + ZIP_SEP + applicationPath; } extractFileFromZip(appPath, domainHome); LOGGER.exiting(CLASS, METHOD); + } /** @@ -1007,6 +1053,18 @@ public String getApplicationPlanArchivePath(String planFile) { return getArchiveName(ARCHIVE_APPS_TARGET_DIR, planFile); } + /** + * Get the archive path of a well formed plan directory in app directory, + * + * @param appName The application name of the app directory + * @param planDir The deployment plan file directory + * @return Archive path for use in the model + */ + public String getApplicationPlanDirArchivePath(String appName, String planDir) { + File zipPath = new File(planDir); + return ARCHIVE_STRUCT_APPS_TARGET_DIR + "/" + appName + "/" + zipPath.getName(); + } + /** * Adds an application's deployment plan file to the archive. * @@ -1441,7 +1499,6 @@ protected String addItemToZip(String zipPathPrefix, File itemToAdd, boolean useF LOGGER.entering(CLASS, METHOD, zipPathPrefix, itemToAdd.getAbsolutePath(), useFileNameInEntryPath); String newName = getArchiveName(zipPathPrefix, itemToAdd.getName(), useFileNameInEntryPath); - if (itemToAdd.isDirectory()) { if (!newName.endsWith(ZIP_SEP)) { newName += ZIP_SEP; @@ -1819,4 +1876,20 @@ private static FileInputStream getFileInputStream(File f, String itemName, Strin return inputStream; } + private String walkDownFolders(String zipPrefix, File zipPath) throws WLSDeployArchiveIOException { + String newSourceName = null; + if (zipPath != null) { + File[] fileList = zipPath.listFiles(); + if (fileList != null) { + for (File item : fileList) { + newSourceName = addItemToZip(zipPrefix, item); + if (item.isDirectory()) { + walkDownFolders(zipPrefix + "/" + item.getName(), item); + } + } + } + } + return newSourceName; + } + } diff --git a/core/src/main/java/oracle/weblogic/deploy/util/XPathUtil.java b/core/src/main/java/oracle/weblogic/deploy/util/XPathUtil.java index cea3e522b3..af1c70fdae 100644 --- a/core/src/main/java/oracle/weblogic/deploy/util/XPathUtil.java +++ b/core/src/main/java/oracle/weblogic/deploy/util/XPathUtil.java @@ -39,7 +39,7 @@ public XPathUtil(String oracleHome){ } public XPathUtil() { - // for testing only + // for use by other types of docs } private static XPathFactory factory = null; diff --git a/core/src/main/python/wlsdeploy/tool/deploy/applications_deployer.py b/core/src/main/python/wlsdeploy/tool/deploy/applications_deployer.py index 0642f4890c..32522b603a 100644 --- a/core/src/main/python/wlsdeploy/tool/deploy/applications_deployer.py +++ b/core/src/main/python/wlsdeploy/tool/deploy/applications_deployer.py @@ -9,10 +9,18 @@ import os from java.io import File from java.io import IOException +from java.io import FileOutputStream from java.security import NoSuchAlgorithmException +from javax.xml.parsers import DocumentBuilderFactory +from javax.xml.transform import OutputKeys +from javax.xml.transform import TransformerFactory +from javax.xml.transform.dom import DOMSource +from javax.xml.transform.stream import StreamResult import oracle.weblogic.deploy.util.FileUtils as FileUtils import oracle.weblogic.deploy.util.PyOrderedDict as OrderedDict +from oracle.weblogic.deploy.util import WLSDeployArchive + from wlsdeploy.aliases import alias_utils from wlsdeploy.aliases import model_constants from wlsdeploy.aliases.location_context import LocationContext @@ -21,6 +29,7 @@ from wlsdeploy.aliases.model_constants import DEPLOYMENT_ORDER from wlsdeploy.aliases.model_constants import LIBRARY from wlsdeploy.aliases.model_constants import PARTITION +from wlsdeploy.aliases.model_constants import PLAN_DIR from wlsdeploy.aliases.model_constants import PLAN_PATH from wlsdeploy.aliases.model_constants import PLAN_STAGING_MODE from wlsdeploy.aliases.model_constants import RESOURCES @@ -183,6 +192,7 @@ def __add_applications(self): self.logger.throwing(ex, class_name=self._class_name, method_name=_method_name) raise ex + application_name = \ self.version_helper.get_application_versioned_name(app_source_path, application_name) @@ -193,6 +203,11 @@ def __add_applications(self): deployer_utils.create_and_cd(application_location, existing_applications, self.aliases) self._set_attributes_and_add_subfolders(application_location, application) application_location.remove_name_token(application_token) + + if app_source_path.startswith(WLSDeployArchive.ARCHIVE_STRUCT_APPS_TARGET_DIR): + plan_dir = dictionary_utils.get_element(application, PLAN_DIR) + self._fix_plan_file(plan_dir) + self.logger.exiting(class_name=self._class_name, method_name=_method_name) def __online_deploy_apps_and_libs(self, base_location): @@ -628,6 +643,10 @@ def __build_app_deploy_strategy(self, location, model_apps, existing_app_refs, s if param in app_dict: self.model_context.replace_tokens(APPLICATION, app, param, app_dict) + if param == SOURCE_PATH and param.startswith(WLSDeployArchive.ARCHIVE_STRUCT_APPS_TARGET_DIR): + plan_dir = dictionary_utils.get_element(app, PLAN_DIR) + self._fix_plan_file(plan_dir) + if model_helper.is_delete_name(app): if self.__verify_delete_versioned_app(app, existing_apps, 'app'): # remove the !app from the model @@ -933,7 +952,6 @@ def __deploy_model_applications(self, model_apps, app_location, deployed_applist path = app_dict[uses_path_tokens_attribute_name] if deployer_utils.is_path_into_archive(path): self.__extract_source_path_from_archive(path, APPLICATION, app_name) - location.add_name_token(token_name, app_name) resource_group_template_name, resource_group_name, partition_name = \ self.__get_mt_names_from_location(location) @@ -1105,6 +1123,29 @@ def __get_deployment_ordering(self, apps): class_name=self._class_name, method_name=_method_name) return result_deploy_order + def _fix_plan_file(self, plan_dir): + #self.archive_helper.extract_directory(plan_dir) + plan_file = os.path.join(self.model_context.get_domain_home(), plan_dir, "plan.xml") + dbf = DocumentBuilderFactory.newInstance() + db = dbf.newDocumentBuilder() + document = db.parse(File(plan_file)) + document.normalizeDocument() + elements = document.getElementsByTagName("config-root") + + if elements is not None and elements.getLength() > 0: + element = elements.item(0) + element.setNodeValue(plan_dir) + element.setTextContent(plan_dir) + ostream = FileOutputStream(plan_file) + transformer_factory = TransformerFactory.newInstance() + transformer = transformer_factory.newTransformer() + transformer.setOutputProperty(OutputKeys.INDENT, "yes") + transformer.setOutputProperty(OutputKeys.STANDALONE, "no") + source = DOMSource(document) + result = StreamResult(ostream) + + transformer.transform(source, result) + def __start_all_apps(self, deployed_app_list, base_location): temp_app_dict = OrderedDict() diff --git a/core/src/main/python/wlsdeploy/tool/discover/deployments_discoverer.py b/core/src/main/python/wlsdeploy/tool/discover/deployments_discoverer.py index 0d9ce96007..7143363809 100644 --- a/core/src/main/python/wlsdeploy/tool/discover/deployments_discoverer.py +++ b/core/src/main/python/wlsdeploy/tool/discover/deployments_discoverer.py @@ -2,6 +2,8 @@ Copyright (c) 2017, 2020, Oracle Corporation and/or its affiliates. All rights reserved. Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. """ +import os + from java.io import File from java.lang import IllegalArgumentException @@ -241,6 +243,10 @@ def _add_application_to_archive(self, application_name, application_dict): _logger.entering(application_name, class_name=_class_name, method_name=_method_name) archive_file = self._model_context.get_archive_file() if model_constants.SOURCE_PATH in application_dict: + if model_constants.PLAN_DIR in application_dict and \ + self._test_app_folder(application_dict[model_constants.SOURCE_PATH], + application_dict[model_constants.PLAN_DIR]): + return self._create_app_folder(application_name, application_dict) file_name = application_dict[model_constants.SOURCE_PATH] if file_name: file_name_path = file_name @@ -260,14 +266,7 @@ def _add_application_to_archive(self, application_name, application_dict): try: new_source_name = archive_file.addApplication(file_name_path) except IllegalArgumentException, iae: - if model_constants.TARGET in application_dict: - target = application_dict[model_constants.TARGET] - del application_dict[model_constants.TARGET] - _logger.warning('WLSDPLY-06395', application_name, target, iae.getLocalizedMessage(), - class_name=_class_name, method_name=_method_name) - else: - _logger.warning('WLSDPLY-06396', application_name, iae.getLocalizedMessage(), - class_name=_class_name, method_name=_method_name) + self._disconnect_target(application_name, application_dict, iae.getLocalizedMessage()) except WLSDeployArchiveIOException, wioe: de = exception_helper.create_discover_exception('WLSDPLY-06397', application_name, file_name_path, wioe.getLocalizedMessage()) @@ -310,14 +309,107 @@ def add_application_plan_to_archive(self, application_name, application_dict): _logger.exiting(class_name=_class_name, method_name=_method_name) return + def _create_app_folder(self, application_name, application_dict): + """ + Create a well-formed application and plan directory + :param application_name: name of application + :param application_dict: model dictionary with application parameters + :return: newly constructed source name + """ + _method_name = '_create_app_folder' + + _logger.entering(application_name, class_name=_class_name, method_name=_method_name) + + self._create_application_directory(application_name, application_dict) + self._create_plan_directory(application_name, application_dict) + + _logger.exiting(class_name=_class_name, method_name=_method_name) + + def _test_app_folder(self, source_path, plan_dir): + app_folder = False + app_dir = File(source_path).getParent() + if app_dir.endswith('app') and plan_dir.endswith('plan'): + app_folder = True + return app_folder + + def _disconnect_target(self, application_name, application_dict, message): + _method_name = '_disconnect_target' + if model_constants.TARGET in application_dict: + target = application_dict[model_constants.TARGET] + del application_dict[model_constants.TARGET] + _logger.warning('WLSDPLY-06395', application_name, target, message, + class_name=_class_name, method_name=_method_name) + else: + _logger.warning('WLSDPLY-06396', application_name, iae.getLocalizedMessage(), + class_name=_class_name, method_name=_method_name) + def _create_application_directory(self, application_name, application_dict): + _method_name = '_create_application_directory' + new_source_name = None + app_dir = application_dict[model_constants.SOURCE_PATH] + archive_file = self._model_context.get_archive_file() + if self._model_context.is_remote(): + new_source_name = archive_file.getApplicationDirectoryArchivePath(application_name, app_dir) + + self.add_to_remote_map(app_dir, new_source_name, + WLSDeployArchive.ArchiveEntryType.APPLICATIONS.name()) + elif not self._model_context.skip_archive(): + if not os.path.abspath(app_dir): + app_dir = os.path.join(self._model_context.get_domain_home(), app_dir) + try: + new_source_name = archive_file.addApplicationFolder(application_name, app_dir) + except IllegalArgumentException, iae: + self._disconnect_target(application_name, application_dict, iae.getLocalizedMessage()) + except WLSDeployArchiveIOException, wioe: + de = exception_helper.create_discover_exception('WLSDPLY-06403', application_name, + file_name_path, wioe.getLocalizedMessage()) + _logger.throwing(class_name=_class_name, method_name=_method_name, error=de) + raise de + if new_source_name is not None: + _logger.finer('WLSDPLY-06398', application_name, new_source_name, class_name=_class_name, + method_name=_method_name) + application_dict[model_constants.SOURCE_PATH] = new_source_name + + def _create_plan_directory(self, application_name, application_dict): + _method_name = '_create_plan_directory' + new_source_name = None + plan_dir = application_dict[model_constants.PLAN_DIR] + archive_file = self._model_context.get_archive_file() + if not os.path.abspath(plan_dir): + plan_dir = os.path.join(self._model_context.get_domain_home(), plan_dir) + if self._model_context.is_remote(): + new_source_name = archive_file.getApplicationPlanDirArchivePath(application_name, plan_dir) + self.add_to_remote_map(plan_dir, new_source_name, + WLSDeployArchive.ArchiveEntryType.APPLICATION_PLAN.name()) + elif not self._model_context.skip_archive(): + try: + new_source_name = archive_file.addApplicationPlanFolder(application_name, plan_dir) + except IllegalArgumentException, iae: + _logger.warning('WLSDPLY-06395', application_name, plan_dir, + iae.getLocalizedMessage(), class_name=_class_name, + method_name=_method_name) + new_source_name = None + except WLSDeployArchiveIOException, wioe: + de = exception_helper.create_discover_exception('WLSDPLY-06397', application_dict, plan_dir, + wioe.getLocalizedMessage()) + _logger.throwing(class_name=_class_name, method_name=_method_name, error=de) + raise de + if new_source_name is not None: + _logger.finer('WLSDPLY-06398', application_name, new_source_name, class_name=_class_name, + method_name=_method_name) + application_dict[model_constants.PLAN_DIR] = new_source_name + def _get_plan_path(self, plan_path, archive_file, app_source_name, application_name, application_dict): + _method_name = '_get_plan_path' plan_dir = None if model_constants.PLAN_DIR in application_dict: plan_dir = application_dict[model_constants.PLAN_DIR] del application_dict[model_constants.PLAN_DIR] plan_file_name = self._resolve_deployment_plan_path(plan_dir, plan_path) if self._model_context.is_remote(): - new_plan_name = archive_file.getApplicationPlanArchivePath(plan_file_name) + if plan_file_name.endswith('plan'): + new_plan_name = archive_file.getApplicationPlanDirArchivePath(plan_file_name) + else: + new_plan_name = archive_file.getApplicationPlanArchivePath(plan_file_name) self.add_to_remote_map(plan_path, new_plan_name, WLSDeployArchive.ArchiveEntryType.APPLICATION_PLAN.name()) elif not self._model_context.skip_archive(): @@ -338,6 +430,7 @@ def _get_plan_path(self, plan_path, archive_file, app_source_name, application_n raise de return new_plan_name + def _resolve_deployment_plan_path(self, plan_dir, plan_path): """ Find the deployment plan absolute file path. diff --git a/core/src/main/resources/oracle/weblogic/deploy/aliases/category_modules/AppDeployment.json b/core/src/main/resources/oracle/weblogic/deploy/aliases/category_modules/AppDeployment.json index 0436b35897..4bf3b9fa9e 100644 --- a/core/src/main/resources/oracle/weblogic/deploy/aliases/category_modules/AppDeployment.json +++ b/core/src/main/resources/oracle/weblogic/deploy/aliases/category_modules/AppDeployment.json @@ -60,7 +60,7 @@ "OnDemandDisplayRefresh": [ {"version": "[10,)", "wlst_mode": "online", "wlst_name": "OnDemandDisplayRefresh", "wlst_path": "WP001", "default_value": "false", "wlst_type": "boolean", "get_method": "GET", "access": "RO" } ], "ParallelDeployModules": [ {"version": "[12.2.1,)", "wlst_mode": "both", "wlst_name": "ParallelDeployModules", "wlst_path": "WP001", "default_value": "false", "wlst_type": "boolean" } ], "PartitionName": [ {"version": "[12.2.1,)", "wlst_mode": "online", "wlst_name": "PartitionName", "wlst_path": "WP001", "default_value": null, "wlst_type": "string" , "get_method": "GET", "access": "RO" } ], - "PlanDir": [ {"version": "[10,)", "wlst_mode": "both", "wlst_name": "PlanDir", "wlst_path": "WP001", "default_value": null, "wlst_type": "string", "access": "${:RO}", "uses_path_tokens": "true" } ], + "PlanDir": [ {"version": "[10,)", "wlst_mode": "both", "wlst_name": "PlanDir", "wlst_path": "WP001", "default_value": null, "wlst_type": "string", "access": "${:ROD}", "uses_path_tokens": "true" } ], "PlanPath": [ {"version": "[10,)", "wlst_mode": "both", "wlst_name": "PlanPath", "wlst_path": "WP001", "default_value": null, "wlst_type": "string", "access": "${:ROD}", "uses_path_tokens": "true" } ], "PlanStagingMode": [ {"version": "[12.1.2,)", "wlst_mode": "both", "wlst_name": "PlanStagingMode", "wlst_path": "WP001", "default_value": null, "wlst_type": "string", "access": "${:ROD}"} ], "RootStagingDir": [ {"version": "[10,)", "wlst_mode": "online", "wlst_name": "RootStagingDir", "wlst_path": "WP001", "default_value": null, "wlst_type": "string", "get_method": "GET", "uses_path_tokens": "true", "access": "RO" } ], diff --git a/core/src/main/resources/oracle/weblogic/deploy/messages/wlsdeploy_rb.properties b/core/src/main/resources/oracle/weblogic/deploy/messages/wlsdeploy_rb.properties index 9b2f1f16cf..a437a6c1b9 100644 --- a/core/src/main/resources/oracle/weblogic/deploy/messages/wlsdeploy_rb.properties +++ b/core/src/main/resources/oracle/weblogic/deploy/messages/wlsdeploy_rb.properties @@ -714,6 +714,7 @@ WLSDPLY-06399=Application {0} plan deployment has new source path location {1} w WLSDPLY-06400=Skipping {0} application {1} WLSDPLY-06401=Skipping {0} shared library {1} WLSDPLY-06402=Add application {0} deployment plan {1} to archive file +WLSDPLY-06403=Unable to add application {0} plan {1} to archive file # domain_info_discoverer.py WLSDPLY-06420=Add the java archive files from the domain library {0} to the archive file