Skip to content

Commit 95f7038

Browse files
rakillenddsharpe
authored andcommitted
Jira WDT-385 Enable exploded app in archive (#512)
* JIRA-WDT-385 Unzip exploded app correctly * JIRA-WDT-385 Read app manifest from file if needed; fix online deployment * JIRA-WDT-385 Use manifest methods to update shared library name
1 parent 55625c1 commit 95f7038

File tree

5 files changed

+149
-51
lines changed

5 files changed

+149
-51
lines changed

core/src/main/java/oracle/weblogic/deploy/util/WLSDeployArchive.java

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2017, 2019, Oracle Corporation and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2017, 2020, Oracle Corporation and/or its affiliates. All rights reserved.
33
* Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl.
44
*/
55
package oracle.weblogic.deploy.util;
@@ -379,6 +379,24 @@ public String extractFile(String path, File extractToLocation) throws WLSDeployA
379379
return extractFile(path, extractToLocation, false);
380380
}
381381

382+
/**
383+
* Extract the specified directory to the specified location (which is typically the domain home). For example,
384+
* if the path is wlsdeploy/applications/myapp and the extractToLocation is the domain home, the directory
385+
* will be written to $DOMAIN_HOME/wlsdeploy/applications/myapp .
386+
*
387+
* @param path the path into the archive file to extract
388+
* @param extractToLocation the base directory to which to write the extracted directory.
389+
* @return the canonical extracted directory name
390+
* @throws WLSDeployArchiveIOException if an error occurs reading the archive or writing the directory
391+
* @throws IllegalArgumentException if the path is null or empty or the extractToLocation
392+
* was not a valid, existing directory
393+
*/
394+
public String extractDirectory(String path, File extractToLocation) throws WLSDeployArchiveIOException {
395+
String result = FileUtils.getCanonicalFile(new File(extractToLocation, path)).getAbsolutePath();
396+
this.extractDirectoryFromZip(path, extractToLocation);
397+
return result;
398+
}
399+
382400
/**
383401
* Extract the specified file to the specified location.
384402
*

core/src/main/python/wlsdeploy/tool/deploy/applications_deployer.py

Lines changed: 98 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,18 @@
11
"""
2-
Copyright (c) 2017, 2019, Oracle Corporation and/or its affiliates. All rights reserved.
2+
Copyright (c) 2017, 2020, Oracle Corporation and/or its affiliates. All rights reserved.
33
Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl.
44
"""
55
import copy
66
import os
77
from java.io import ByteArrayOutputStream
88
from java.io import File
9+
from java.io import FileInputStream
910
from java.io import FileNotFoundException
1011
from java.io import IOException
1112
from java.lang import IllegalStateException
1213
from java.security import NoSuchAlgorithmException
1314
from java.util.jar import JarFile
15+
from java.util.jar import Manifest
1416
from java.util.zip import ZipException
1517
from sets import Set
1618
from wlsdeploy.aliases.location_context import LocationContext
@@ -164,7 +166,7 @@ def __add_applications(self):
164166

165167
if deployer_utils.is_path_into_archive(app_source_path):
166168
if self.archive_helper is not None:
167-
self.archive_helper.extract_file(app_source_path)
169+
self.__extract_source_path_from_archive(app_source_path, APPLICATION, application_name)
168170
else:
169171
ex = exception_helper.create_deploy_exception('WLSDPLY-09303', application_name)
170172
self.logger.throwing(ex, class_name=self._class_name, method_name=_method_name)
@@ -567,14 +569,23 @@ def __build_app_deploy_strategy(self, location, model_apps, existing_apps, exist
567569
self.model_context.replace_tokens(APPLICATION, app, param, app_dict)
568570

569571
if app in existing_apps:
572+
# Compare the hashes of the domain's existing apps to the model's apps.
573+
# If they match, remove them from the list to be deployed.
574+
# If they are different, stop and un-deploy the app, and leave it in the list.
575+
570576
existing_app_ref = dictionary_utils.get_dictionary_element(existing_app_refs, app)
571577
plan_path = dictionary_utils.get_element(existing_app_ref, 'planPath')
572578
src_path = dictionary_utils.get_element(existing_app_ref, 'sourcePath')
573579

574-
model_src_hash = \
575-
self.__get_hash(dictionary_utils.get_element(app_dict, SOURCE_PATH))
576-
model_plan_hash = \
577-
self.__get_hash(dictionary_utils.get_element(app_dict, PLAN_PATH))
580+
model_src_path = dictionary_utils.get_element(app_dict, SOURCE_PATH)
581+
582+
if (model_src_path is not None) and deployer_utils.is_path_into_archive(model_src_path) \
583+
and self.archive_helper.contains_path(model_src_path):
584+
model_src_hash = -1 # don't calculate hash, just ensure that hashes will not match
585+
else:
586+
model_src_hash = self.__get_hash(model_src_path)
587+
588+
model_plan_hash = self.__get_hash(dictionary_utils.get_element(app_dict, PLAN_PATH))
578589

579590
existing_src_hash = self.__get_file_hash(src_path)
580591
existing_plan_hash = self.__get_file_hash(plan_path)
@@ -603,6 +614,10 @@ def __get_file_hash(self, filename):
603614
try:
604615
if filename is None:
605616
return None
617+
618+
if File(filename).isDirectory(): # can't calculate for exploded apps, libraries, etc.
619+
return None
620+
606621
hash_value = FileUtils.computeHash(filename)
607622
except (IOException, NoSuchAlgorithmException), e:
608623
ex = exception_helper.create_deploy_exception('WLSDPLY-09309', filename, e.getLocalizedMessage(), error=e)
@@ -734,7 +749,8 @@ def __deploy_model_applications(self, model_apps, app_location, deployed_applist
734749
options = _get_deploy_options(model_apps, app_name, library_module='false')
735750
for uses_path_tokens_attribute_name in uses_path_tokens_attribute_names:
736751
if uses_path_tokens_attribute_name in app_dict:
737-
self.__extract_file_from_archive(app_dict[uses_path_tokens_attribute_name])
752+
self.__extract_source_path_from_archive(app_dict[uses_path_tokens_attribute_name],
753+
APPLICATION, app_name)
738754

739755
location.add_name_token(token_name, app_name)
740756
resource_group_template_name, resource_group_name, partition_name = \
@@ -823,6 +839,31 @@ def __extract_file_from_archive(self, path):
823839
self.archive_helper.extract_file(path)
824840
return
825841

842+
def __extract_source_path_from_archive(self, source_path, model_type, model_name):
843+
"""
844+
Extract contents from the archive set for the specified source path.
845+
The contents may be a single file, or a directory with exploded content.
846+
:param source_path: the path to be extracted (previously checked to be under wlsdeploy)
847+
:param model_type: the model type (Application, etc.), used for logging
848+
:param model_name: the element name (my-app, etc.), used for logging
849+
"""
850+
_method_name = '__extract_source_path_from_archive'
851+
852+
# source path may be may be a single file (jar, war, etc.)
853+
if self.archive_helper.contains_file(source_path):
854+
self.archive_helper.extract_file(source_path)
855+
856+
# source path may be exploded directory in archive
857+
elif self.archive_helper.contains_path(source_path):
858+
self.archive_helper.extract_directory(source_path)
859+
860+
else:
861+
ex = exception_helper.create_deploy_exception('WLSDPLY-09330', model_type, model_name, source_path)
862+
self.logger.throwing(ex, class_name=self._class_name, method_name=_method_name)
863+
raise ex
864+
865+
return
866+
826867
def __get_deployable_library_versioned_name(self, source_path, model_name):
827868
"""
828869
Get the proper name of the deployable library that WLST requires in the target domain. This method is
@@ -840,49 +881,28 @@ def __get_deployable_library_versioned_name(self, source_path, model_name):
840881

841882
old_name_tuple = deployer_utils.get_library_name_components(model_name, self.wlst_mode)
842883
try:
884+
versioned_name = old_name_tuple[self._EXTENSION_INDEX]
843885
source_path = self.model_context.replace_token_string(source_path)
844-
archive = JarFile(source_path)
845-
manifest_object = archive.getManifest()
846-
tokens = []
847-
if manifest_object is not None:
848-
bao = ByteArrayOutputStream()
849-
manifest_object.write(bao)
850-
manifest = bao.toString('UTF-8')
851-
tokens = manifest.split()
852-
853-
if 'Extension-Name:' in tokens:
854-
extension_index = tokens.index('Extension-Name:')
855-
if len(tokens) > extension_index:
856-
versioned_name = tokens[extension_index + 1]
857-
else:
858-
ex = exception_helper.create_deploy_exception('WLSDPLY-09321', model_name, source_path, tokens)
859-
self.logger.throwing(ex, class_name=self._class_name, method_name=_method_name)
860-
raise ex
861-
else:
862-
versioned_name = old_name_tuple[self._EXTENSION_INDEX]
886+
manifest = self.__get_manifest(source_path)
887+
if manifest is not None:
888+
attributes = manifest.getMainAttributes()
889+
890+
extension_name = attributes.getValue("Extension-Name")
891+
if not string_utils.is_empty(extension_name):
892+
versioned_name = extension_name
863893

864-
if 'Specification-Version:' in tokens:
865-
spec_index = tokens.index('Specification-Version:')
866-
if len(tokens) > spec_index:
867-
versioned_name += '#' + tokens[spec_index + 1]
894+
specification_version = attributes.getValue("Specification-Version")
895+
if not string_utils.is_empty(specification_version):
896+
versioned_name += '#' + specification_version
868897

869898
# Cannot specify an impl version without a spec version
870-
if 'Implementation-Version:' in tokens:
871-
impl_index = tokens.index('Implementation-Version:')
872-
if len(tokens) > impl_index:
873-
versioned_name += '@' + tokens[impl_index + 1]
874-
else:
875-
ex = exception_helper.create_deploy_exception('WLSDPLY-09322', model_name,
876-
source_path, tokens)
877-
self.logger.throwing(ex, class_name=self._class_name, method_name=_method_name)
878-
raise ex
879-
else:
880-
ex = exception_helper.create_deploy_exception('WLSDPLY-09323', model_name, source_path, tokens)
881-
self.logger.throwing(ex, class_name=self._class_name, method_name=_method_name)
882-
raise ex
899+
implementation_version = attributes.getValue("Implementation-Version")
900+
if not string_utils.is_empty(implementation_version):
901+
versioned_name += '@' + implementation_version
902+
903+
self.logger.info('WLSDPLY-09324', model_name, versioned_name,
904+
class_name=self._class_name, method_name=_method_name)
883905

884-
self.logger.info('WLSDPLY-09324', model_name, versioned_name,
885-
class_name=self._class_name, method_name=_method_name)
886906
except (IOException, FileNotFoundException, ZipException, IllegalStateException), e:
887907
ex = exception_helper.create_deploy_exception('WLSDPLY-09325', model_name, source_path, str(e), error=e)
888908
self.logger.throwing(ex, class_name=self._class_name, method_name=_method_name)
@@ -912,8 +932,8 @@ def __get_deployable_application_versioned_name(self, source_path, model_name):
912932

913933
try:
914934
source_path = self.model_context.replace_token_string(source_path)
915-
archive = JarFile(source_path)
916-
manifest = archive.getManifest()
935+
manifest = self.__get_manifest(source_path)
936+
917937
if manifest is not None:
918938
attributes = manifest.getMainAttributes()
919939
application_version = attributes.getValue(self._APP_VERSION_MANIFEST_KEY)
@@ -930,6 +950,37 @@ def __get_deployable_application_versioned_name(self, source_path, model_name):
930950
self.logger.exiting(class_name=self._class_name, method_name=_method_name, result=versioned_name)
931951
return versioned_name
932952

953+
def __get_manifest(self, source_path):
954+
"""
955+
Returns the manifest object for the specified path.
956+
The source path may be a jar, or an exploded path.
957+
:param source_path: the source path to be checked
958+
:return: the manifest, or None if it is not present
959+
:raises: IOException: if there are problems reading an existing manifest
960+
"""
961+
source_path_file = File(source_path)
962+
manifest = None
963+
964+
if source_path_file.isDirectory():
965+
manifest_file = File(source_path_file, "META-INF/MANIFEST.MF")
966+
if manifest_file.exists():
967+
stream = None
968+
try:
969+
stream = FileInputStream(manifest_file)
970+
manifest = Manifest(stream)
971+
finally:
972+
if stream is not None:
973+
try:
974+
stream.close()
975+
except IOException:
976+
# nothing to report
977+
pass
978+
else:
979+
archive = JarFile(source_path)
980+
manifest = archive.getManifest()
981+
982+
return manifest
983+
933984
def __get_deployment_ordering(self, apps):
934985
_method_name = '__get_deployment_ordering'
935986
name_sorted_keys = apps.keys()

core/src/main/python/wlsdeploy/tool/deploy/deployer.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
"""
2-
Copyright (c) 2017, 2019, Oracle Corporation and/or its affiliates. All rights reserved.
2+
Copyright (c) 2017, 2020, Oracle Corporation and/or its affiliates. All rights reserved.
33
Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl.
44
"""
55
import os
@@ -385,6 +385,9 @@ def __process_archive_entry(self, location, key, value):
385385
# The file does not exist so extract it from the archive.
386386
self.archive_helper.extract_file(value)
387387
result = True
388+
elif self.archive_helper.contains_path(value):
389+
# contents should have been extracted elsewhere, such as for apps and shared libraries
390+
result = self.__process_directory_entry(fullpath)
388391
elif value.endswith('/'):
389392
# If the path is a directory in the wlsdeploy directory tree
390393
# but not in the archive, just create it to help the user.

core/src/main/python/wlsdeploy/tool/util/archive_helper.py

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
"""
2-
Copyright (c) 2017, 2019, Oracle Corporation and/or its affiliates. All rights reserved.
2+
Copyright (c) 2017, 2020, Oracle Corporation and/or its affiliates. All rights reserved.
33
Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl.
44
"""
55
from java.io import File
@@ -206,6 +206,32 @@ def extract_file(self, path, location=None):
206206
self.__logger.exiting(class_name=self.__class_name, method_name=_method_name, result=result)
207207
return result
208208

209+
def extract_directory(self, path, location=None):
210+
"""
211+
Extract the specified directory from the archive into the specified directory, or into Domain Home.
212+
:param path: the path into the archive
213+
:param location: the location to which to extract the directory
214+
:return: path to the extracted directory
215+
:raises: BundleAwareException of the appropriate type: if an error occurs
216+
"""
217+
_method_name = 'extract_directory'
218+
self.__logger.entering(path, class_name=self.__class_name, method_name=_method_name)
219+
220+
try:
221+
archive_file = self._find_archive_for_path(path, True)
222+
if location is None:
223+
result = archive_file.extractDirectory(path, self.__domain_home)
224+
else:
225+
extract_location = FileUtils.getCanonicalFile(File(location))
226+
result = archive_file.extractDirectory(path, extract_location, True)
227+
except (IllegalArgumentException, WLSDeployArchiveIOException), e:
228+
ex = exception_helper.create_exception(self.__exception_type, "WLSDPLY-19303", path,
229+
self.__archive_files_text, e.getLocalizedMessage(), error=e)
230+
self.__logger.throwing(ex, class_name=self.__class_name, method_name=_method_name)
231+
raise ex
232+
self.__logger.exiting(class_name=self.__class_name, method_name=_method_name, result=result)
233+
return result
234+
209235
def get_file_hash(self, path):
210236
"""
211237
Get the Base64-encoded hash value for the file at the specified path within the archive.

core/src/main/resources/oracle/weblogic/deploy/messages/wlsdeploy_rb.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -991,7 +991,7 @@ WLSDPLY-09326=Deployment order is {0}
991991
WLSDPLY-09327=Failed to stop application {0}: {1}
992992
WLSDPLY-09328=Application {0} name has been updated to {1} to match the application version in the MANIFEST.MF file
993993
WLSDPLY-09329=Failed to compute name for application {0} from archive at {1}: {2}
994-
994+
WLSDPLY-09330=Source path for {0} {1} not found in any archive: {2}
995995

996996
# wlsdeploy/tool/deploy/common_resources_deployer.py
997997
WLSDPLY-09400=ResourceGroup was specified in the test file but are not supported in WebLogic Server version {0}

0 commit comments

Comments
 (0)