mirror of
https://github.com/openjdk/jdk.git
synced 2026-03-23 14:19:56 +00:00
8211723: AppCDS: referring to a jar file in any way other than exactly how it was done during dumping doesn't work
Replaced os::file_name_strncmp() with os::same_files(). Reviewed-by: iklam, jiangli
This commit is contained in:
parent
302b8d06ce
commit
5d1361df03
@ -37,11 +37,6 @@
|
||||
#include <sys/ioctl.h>
|
||||
#include <netdb.h>
|
||||
|
||||
// File names are case-insensitive on windows only.
|
||||
inline int os::file_name_strncmp(const char* s1, const char* s2, size_t num) {
|
||||
return strncmp(s1, s2, num);
|
||||
}
|
||||
|
||||
inline bool os::uses_stack_guard_pages() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -35,11 +35,6 @@
|
||||
#include <poll.h>
|
||||
#include <netdb.h>
|
||||
|
||||
// File names are case-insensitive on windows only
|
||||
inline int os::file_name_strncmp(const char* s1, const char* s2, size_t num) {
|
||||
return strncmp(s1, s2, num);
|
||||
}
|
||||
|
||||
inline bool os::uses_stack_guard_pages() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -35,11 +35,6 @@
|
||||
#include <poll.h>
|
||||
#include <netdb.h>
|
||||
|
||||
// File names are case-insensitive on windows only
|
||||
inline int os::file_name_strncmp(const char* s1, const char* s2, size_t num) {
|
||||
return strncmp(s1, s2, num);
|
||||
}
|
||||
|
||||
inline bool os::uses_stack_guard_pages() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1450,6 +1450,30 @@ char * os::native_path(char *path) {
|
||||
return path;
|
||||
}
|
||||
|
||||
bool os::same_files(const char* file1, const char* file2) {
|
||||
if (strcmp(file1, file2) == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool is_same = false;
|
||||
struct stat st1;
|
||||
struct stat st2;
|
||||
|
||||
if (os::stat(file1, &st1) < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (os::stat(file2, &st2) < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino) {
|
||||
// same files
|
||||
is_same = true;
|
||||
}
|
||||
return is_same;
|
||||
}
|
||||
|
||||
// Check minimum allowable stack sizes for thread creation and to initialize
|
||||
// the java system classes, including StackOverflowError - depends on page
|
||||
// size.
|
||||
|
||||
@ -37,11 +37,6 @@
|
||||
#include <netdb.h>
|
||||
#include <setjmp.h>
|
||||
|
||||
// File names are case-insensitive on windows only
|
||||
inline int os::file_name_strncmp(const char* s1, const char* s2, size_t num) {
|
||||
return strncmp(s1, s2, num);
|
||||
}
|
||||
|
||||
inline bool os::uses_stack_guard_pages() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -4367,6 +4367,88 @@ int os::stat(const char *path, struct stat *sbuf) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
static HANDLE create_read_only_file_handle(const char* file) {
|
||||
if (file == NULL) {
|
||||
return INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
char* nativepath = (char*)os::strdup(file, mtInternal);
|
||||
if (nativepath == NULL) {
|
||||
errno = ENOMEM;
|
||||
return INVALID_HANDLE_VALUE;
|
||||
}
|
||||
os::native_path(nativepath);
|
||||
|
||||
size_t len = strlen(nativepath);
|
||||
HANDLE handle = INVALID_HANDLE_VALUE;
|
||||
|
||||
if (len < MAX_PATH) {
|
||||
handle = ::CreateFile(nativepath, 0, FILE_SHARE_READ,
|
||||
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
} else {
|
||||
errno_t err = ERROR_SUCCESS;
|
||||
wchar_t* wfile = create_unc_path(nativepath, err);
|
||||
if (err != ERROR_SUCCESS) {
|
||||
if (wfile != NULL) {
|
||||
destroy_unc_path(wfile);
|
||||
}
|
||||
os::free(nativepath);
|
||||
return INVALID_HANDLE_VALUE;
|
||||
}
|
||||
handle = ::CreateFileW(wfile, 0, FILE_SHARE_READ,
|
||||
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
destroy_unc_path(wfile);
|
||||
}
|
||||
|
||||
os::free(nativepath);
|
||||
return handle;
|
||||
}
|
||||
|
||||
bool os::same_files(const char* file1, const char* file2) {
|
||||
|
||||
if (file1 == NULL && file2 == NULL) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (file1 == NULL || file2 == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (strcmp(file1, file2) == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
HANDLE handle1 = create_read_only_file_handle(file1);
|
||||
HANDLE handle2 = create_read_only_file_handle(file2);
|
||||
bool result = false;
|
||||
|
||||
// if we could open both paths...
|
||||
if (handle1 != INVALID_HANDLE_VALUE && handle2 != INVALID_HANDLE_VALUE) {
|
||||
BY_HANDLE_FILE_INFORMATION fileInfo1;
|
||||
BY_HANDLE_FILE_INFORMATION fileInfo2;
|
||||
if (::GetFileInformationByHandle(handle1, &fileInfo1) &&
|
||||
::GetFileInformationByHandle(handle2, &fileInfo2)) {
|
||||
// the paths are the same if they refer to the same file (fileindex) on the same volume (volume serial number)
|
||||
if (fileInfo1.dwVolumeSerialNumber == fileInfo2.dwVolumeSerialNumber &&
|
||||
fileInfo1.nFileIndexHigh == fileInfo2.nFileIndexHigh &&
|
||||
fileInfo1.nFileIndexLow == fileInfo2.nFileIndexLow) {
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//free the handles
|
||||
if (handle1 != INVALID_HANDLE_VALUE) {
|
||||
::CloseHandle(handle1);
|
||||
}
|
||||
|
||||
if (handle2 != INVALID_HANDLE_VALUE) {
|
||||
::CloseHandle(handle2);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
#define FT2INT64(ft) \
|
||||
((jlong)((jlong)(ft).dwHighDateTime << 32 | (julong)(ft).dwLowDateTime))
|
||||
|
||||
@ -32,11 +32,6 @@ inline const char* os::dll_file_extension() { return ".dll"; }
|
||||
|
||||
inline const int os::default_file_open_flags() { return O_BINARY | O_NOINHERIT;}
|
||||
|
||||
// File names are case-insensitive on windows only
|
||||
inline int os::file_name_strncmp(const char* s, const char* t, size_t num) {
|
||||
return _strnicmp(s, t, num);
|
||||
}
|
||||
|
||||
inline void os::dll_unload(void *lib) {
|
||||
::FreeLibrary((HMODULE)lib);
|
||||
}
|
||||
|
||||
@ -292,11 +292,13 @@ ClassFileStream* ClassPathDirEntry::open_stream(const char* name, TRAPS) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ClassPathZipEntry::ClassPathZipEntry(jzfile* zip, const char* zip_name, bool is_boot_append) : ClassPathEntry() {
|
||||
ClassPathZipEntry::ClassPathZipEntry(jzfile* zip, const char* zip_name,
|
||||
bool is_boot_append, bool from_class_path_attr) : ClassPathEntry() {
|
||||
_zip = zip;
|
||||
char *copy = NEW_C_HEAP_ARRAY(char, strlen(zip_name)+1, mtClass);
|
||||
strcpy(copy, zip_name);
|
||||
_zip_name = copy;
|
||||
_from_class_path_attr = from_class_path_attr;
|
||||
}
|
||||
|
||||
ClassPathZipEntry::~ClassPathZipEntry() {
|
||||
@ -577,7 +579,7 @@ void ClassLoader::setup_app_search_path(const char *class_path) {
|
||||
strncpy(path, &class_path[start], end - start);
|
||||
path[end - start] = '\0';
|
||||
|
||||
update_class_path_entry_list(path, false, false);
|
||||
update_class_path_entry_list(path, false, false, false);
|
||||
|
||||
while (class_path[end] == os::path_separator()[0]) {
|
||||
end++;
|
||||
@ -612,7 +614,7 @@ void ClassLoader::update_module_path_entry_list(const char *path, TRAPS) {
|
||||
// File or directory found
|
||||
ClassPathEntry* new_entry = NULL;
|
||||
new_entry = create_class_path_entry(path, &st, true /* throw_exception */,
|
||||
false /*is_boot_append */, CHECK);
|
||||
false /*is_boot_append */, false /* from_class_path_attr */, CHECK);
|
||||
if (new_entry == NULL) {
|
||||
return;
|
||||
}
|
||||
@ -668,7 +670,7 @@ void ClassLoader::setup_patch_mod_entries() {
|
||||
struct stat st;
|
||||
if (os::stat(path, &st) == 0) {
|
||||
// File or directory found
|
||||
ClassPathEntry* new_entry = create_class_path_entry(path, &st, false, false, CHECK);
|
||||
ClassPathEntry* new_entry = create_class_path_entry(path, &st, false, false, false, CHECK);
|
||||
// If the path specification is valid, enter it into this module's list
|
||||
if (new_entry != NULL) {
|
||||
module_cpl->add_to_list(new_entry);
|
||||
@ -737,7 +739,7 @@ void ClassLoader::setup_boot_search_path(const char *class_path) {
|
||||
struct stat st;
|
||||
if (os::stat(path, &st) == 0) {
|
||||
// Directory found
|
||||
ClassPathEntry* new_entry = create_class_path_entry(path, &st, false, false, CHECK);
|
||||
ClassPathEntry* new_entry = create_class_path_entry(path, &st, false, false, false, CHECK);
|
||||
|
||||
// Check for a jimage
|
||||
if (Arguments::has_jimage()) {
|
||||
@ -754,7 +756,7 @@ void ClassLoader::setup_boot_search_path(const char *class_path) {
|
||||
} else {
|
||||
// Every entry on the system boot class path after the initial base piece,
|
||||
// which is set by os::set_boot_path(), is considered an appended entry.
|
||||
update_class_path_entry_list(path, false, true);
|
||||
update_class_path_entry_list(path, false, true, false);
|
||||
}
|
||||
|
||||
while (class_path[end] == os::path_separator()[0]) {
|
||||
@ -782,7 +784,7 @@ void ClassLoader::add_to_exploded_build_list(Symbol* module_sym, TRAPS) {
|
||||
struct stat st;
|
||||
if (os::stat(path, &st) == 0) {
|
||||
// Directory found
|
||||
ClassPathEntry* new_entry = create_class_path_entry(path, &st, false, false, CHECK);
|
||||
ClassPathEntry* new_entry = create_class_path_entry(path, &st, false, false, false, CHECK);
|
||||
|
||||
// If the path specification is valid, enter it into this module's list.
|
||||
// There is no need to check for duplicate modules in the exploded entry list,
|
||||
@ -802,7 +804,9 @@ void ClassLoader::add_to_exploded_build_list(Symbol* module_sym, TRAPS) {
|
||||
|
||||
ClassPathEntry* ClassLoader::create_class_path_entry(const char *path, const struct stat* st,
|
||||
bool throw_exception,
|
||||
bool is_boot_append, TRAPS) {
|
||||
bool is_boot_append,
|
||||
bool from_class_path_attr,
|
||||
TRAPS) {
|
||||
JavaThread* thread = JavaThread::current();
|
||||
ClassPathEntry* new_entry = NULL;
|
||||
if ((st->st_mode & S_IFMT) == S_IFREG) {
|
||||
@ -832,7 +836,7 @@ ClassPathEntry* ClassLoader::create_class_path_entry(const char *path, const str
|
||||
zip = (*ZipOpen)(canonical_path, &error_msg);
|
||||
}
|
||||
if (zip != NULL && error_msg == NULL) {
|
||||
new_entry = new ClassPathZipEntry(zip, path, is_boot_append);
|
||||
new_entry = new ClassPathZipEntry(zip, path, is_boot_append, from_class_path_attr);
|
||||
} else {
|
||||
char *msg;
|
||||
if (error_msg == NULL) {
|
||||
@ -882,7 +886,7 @@ ClassPathZipEntry* ClassLoader::create_class_path_zip_entry(const char *path, bo
|
||||
}
|
||||
if (zip != NULL && error_msg == NULL) {
|
||||
// create using canonical path
|
||||
return new ClassPathZipEntry(zip, canonical_path, is_boot_append);
|
||||
return new ClassPathZipEntry(zip, canonical_path, is_boot_append, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -956,13 +960,14 @@ void ClassLoader::add_to_app_classpath_entries(const char* path,
|
||||
bool ClassLoader::update_class_path_entry_list(const char *path,
|
||||
bool check_for_duplicates,
|
||||
bool is_boot_append,
|
||||
bool from_class_path_attr,
|
||||
bool throw_exception) {
|
||||
struct stat st;
|
||||
if (os::stat(path, &st) == 0) {
|
||||
// File or directory found
|
||||
ClassPathEntry* new_entry = NULL;
|
||||
Thread* THREAD = Thread::current();
|
||||
new_entry = create_class_path_entry(path, &st, throw_exception, is_boot_append, CHECK_(false));
|
||||
new_entry = create_class_path_entry(path, &st, throw_exception, is_boot_append, from_class_path_attr, CHECK_(false));
|
||||
if (new_entry == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -53,6 +53,8 @@ public:
|
||||
void set_next(ClassPathEntry* next);
|
||||
virtual bool is_modules_image() const = 0;
|
||||
virtual bool is_jar_file() const = 0;
|
||||
// Is this entry created from the "Class-path" attribute from a JAR Manifest?
|
||||
virtual bool from_class_path_attr() const = 0;
|
||||
virtual const char* name() const = 0;
|
||||
virtual JImageFile* jimage() const = 0;
|
||||
virtual void close_jimage() = 0;
|
||||
@ -73,6 +75,7 @@ class ClassPathDirEntry: public ClassPathEntry {
|
||||
public:
|
||||
bool is_modules_image() const { return false; }
|
||||
bool is_jar_file() const { return false; }
|
||||
bool from_class_path_attr() const { return false; }
|
||||
const char* name() const { return _dir; }
|
||||
JImageFile* jimage() const { return NULL; }
|
||||
void close_jimage() {}
|
||||
@ -99,13 +102,15 @@ class ClassPathZipEntry: public ClassPathEntry {
|
||||
private:
|
||||
jzfile* _zip; // The zip archive
|
||||
const char* _zip_name; // Name of zip archive
|
||||
bool _from_class_path_attr; // From the "Class-path" attribute of a jar file
|
||||
public:
|
||||
bool is_modules_image() const { return false; }
|
||||
bool is_jar_file() const { return true; }
|
||||
bool from_class_path_attr() const { return _from_class_path_attr; }
|
||||
const char* name() const { return _zip_name; }
|
||||
JImageFile* jimage() const { return NULL; }
|
||||
void close_jimage() {}
|
||||
ClassPathZipEntry(jzfile* zip, const char* zip_name, bool is_boot_append);
|
||||
ClassPathZipEntry(jzfile* zip, const char* zip_name, bool is_boot_append, bool from_class_path_attr);
|
||||
virtual ~ClassPathZipEntry();
|
||||
u1* open_entry(const char* name, jint* filesize, bool nul_terminate, TRAPS);
|
||||
ClassFileStream* open_stream(const char* name, TRAPS);
|
||||
@ -122,6 +127,7 @@ private:
|
||||
public:
|
||||
bool is_modules_image() const;
|
||||
bool is_jar_file() const { return false; }
|
||||
bool from_class_path_attr() const { return false; }
|
||||
bool is_open() const { return _jimage != NULL; }
|
||||
const char* name() const { return _name == NULL ? "" : _name; }
|
||||
JImageFile* jimage() const { return _jimage; }
|
||||
@ -257,7 +263,8 @@ class ClassLoader: AllStatic {
|
||||
public:
|
||||
static ClassPathEntry* create_class_path_entry(const char *path, const struct stat* st,
|
||||
bool throw_exception,
|
||||
bool is_boot_append, TRAPS);
|
||||
bool is_boot_append,
|
||||
bool from_class_path_attr, TRAPS);
|
||||
|
||||
// If the package for the fully qualified class name is in the boot
|
||||
// loader's package entry table then add_package() sets the classpath_index
|
||||
@ -281,6 +288,7 @@ class ClassLoader: AllStatic {
|
||||
static bool update_class_path_entry_list(const char *path,
|
||||
bool check_for_duplicates,
|
||||
bool is_boot_append,
|
||||
bool from_class_path_attr,
|
||||
bool throw_exception=true);
|
||||
CDS_ONLY(static void update_module_path_entry_list(const char *path, TRAPS);)
|
||||
static void print_bootclasspath();
|
||||
|
||||
@ -213,7 +213,7 @@ void ClassLoaderExt::process_jar_manifest(ClassPathEntry* entry,
|
||||
int n = os::snprintf(libname, libname_len + 1, "%.*s%s", dir_len, dir_name, file_start);
|
||||
assert((size_t)n == libname_len, "Unexpected number of characters in string");
|
||||
trace_class_path("library = ", libname);
|
||||
ClassLoader::update_class_path_entry_list(libname, true, false);
|
||||
ClassLoader::update_class_path_entry_list(libname, true, false, true /* from_class_path_attr */);
|
||||
}
|
||||
|
||||
file_start = file_end;
|
||||
@ -339,7 +339,7 @@ ClassPathEntry* ClassLoaderExt::find_classpath_entry_from_cache(const char* path
|
||||
}
|
||||
ClassPathEntry* new_entry = NULL;
|
||||
|
||||
new_entry = create_class_path_entry(path, &st, false, false, CHECK_NULL);
|
||||
new_entry = create_class_path_entry(path, &st, false, false, false, CHECK_NULL);
|
||||
if (new_entry == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -153,83 +153,10 @@ bool SharedPathsMiscInfo::check(bool is_static) {
|
||||
return true;
|
||||
}
|
||||
|
||||
char* skip_first_path_entry(const char* path) {
|
||||
size_t path_sep_len = strlen(os::path_separator());
|
||||
char* p = strstr((char*)path, os::path_separator());
|
||||
if (p != NULL) {
|
||||
debug_only( {
|
||||
size_t image_name_len = strlen(MODULES_IMAGE_NAME);
|
||||
assert(strncmp(p - image_name_len, MODULES_IMAGE_NAME, image_name_len) == 0,
|
||||
"first entry must be the modules image");
|
||||
} );
|
||||
p += path_sep_len;
|
||||
} else {
|
||||
debug_only( {
|
||||
assert(ClassLoader::string_ends_with(path, MODULES_IMAGE_NAME),
|
||||
"first entry must be the modules image");
|
||||
} );
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
bool SharedPathsMiscInfo::check(jint type, const char* path, bool is_static) {
|
||||
assert(UseSharedSpaces, "runtime only");
|
||||
switch (type) {
|
||||
case BOOT_PATH:
|
||||
{
|
||||
//
|
||||
// - Archive contains boot classes only - relaxed boot path check:
|
||||
// Extra path elements appended to the boot path at runtime are allowed.
|
||||
//
|
||||
// - Archive contains application or platform classes - strict boot path check:
|
||||
// Validate the entire runtime boot path, which must be compactible
|
||||
// with the dump time boot path. Appending boot path at runtime is not
|
||||
// allowed.
|
||||
//
|
||||
|
||||
// The first entry in boot path is the modules_image (guaranteed by
|
||||
// ClassLoader::setup_boot_search_path()). Skip the first entry. The
|
||||
// path of the runtime modules_image may be different from the dump
|
||||
// time path (e.g. the JDK image is copied to a different location
|
||||
// after generating the shared archive), which is acceptable. For most
|
||||
// common cases, the dump time boot path might contain modules_image only.
|
||||
char* runtime_boot_path = Arguments::get_sysclasspath();
|
||||
char* rp = skip_first_path_entry(runtime_boot_path);
|
||||
char* dp = skip_first_path_entry(path);
|
||||
|
||||
bool relaxed_check = is_static ?
|
||||
!FileMapInfo::current_info()->header()->has_platform_or_app_classes() :
|
||||
!FileMapInfo::dynamic_info()->header()->has_platform_or_app_classes();
|
||||
if (dp == NULL && rp == NULL) {
|
||||
break; // ok, both runtime and dump time boot paths have modules_images only
|
||||
} else if (dp == NULL && rp != NULL && relaxed_check) {
|
||||
break; // ok, relaxed check, runtime has extra boot append path entries
|
||||
} else if (dp != NULL && rp != NULL) {
|
||||
size_t num;
|
||||
size_t dp_len = strlen(dp);
|
||||
size_t rp_len = strlen(rp);
|
||||
if (rp_len >= dp_len) {
|
||||
if (relaxed_check) {
|
||||
// only check the leading entries in the runtime boot path, up to
|
||||
// the length of the dump time boot path
|
||||
num = dp_len;
|
||||
} else {
|
||||
// check the full runtime boot path, must match with dump time
|
||||
num = rp_len;
|
||||
}
|
||||
|
||||
if (os::file_name_strncmp(dp, rp, num) == 0) {
|
||||
// make sure it is the end of an entry in the runtime boot path
|
||||
if (rp[dp_len] == '\0' || rp[dp_len] == os::path_separator()[0]) {
|
||||
break; // ok, runtime and dump time paths match
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The paths are different
|
||||
return fail("[BOOT classpath mismatch, actual =", runtime_boot_path);
|
||||
}
|
||||
break;
|
||||
case NON_EXIST:
|
||||
{
|
||||
@ -242,22 +169,6 @@ bool SharedPathsMiscInfo::check(jint type, const char* path, bool is_static) {
|
||||
}
|
||||
break;
|
||||
case APP_PATH:
|
||||
{
|
||||
size_t len = strlen(path);
|
||||
const char *appcp = Arguments::get_appclasspath();
|
||||
assert(appcp != NULL, "NULL app classpath");
|
||||
size_t appcp_len = strlen(appcp);
|
||||
if (appcp_len < len) {
|
||||
return fail("Run time APP classpath is shorter than the one at dump time: ", appcp);
|
||||
}
|
||||
// Prefix is OK: E.g., dump with -cp foo.jar, but run with -cp foo.jar:bar.jar.
|
||||
if (os::file_name_strncmp(path, appcp, len) != 0) {
|
||||
return fail("[APP classpath mismatch, actual: -Djava.class.path=", appcp);
|
||||
}
|
||||
if (appcp[len] != '\0' && appcp[len] != os::path_separator()[0]) {
|
||||
return fail("Dump time APP classpath is not a proper prefix of run time APP classpath: ", appcp);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return fail("Corrupted archive file header");
|
||||
|
||||
@ -75,7 +75,7 @@ extern address JVM_FunctionAtEnd();
|
||||
// an archive file should stop the process. Unrecoverable errors during
|
||||
// the reading of the archive file should stop the process.
|
||||
|
||||
static void fail(const char *msg, va_list ap) {
|
||||
static void fail_exit(const char *msg, va_list ap) {
|
||||
// This occurs very early during initialization: tty is not initialized.
|
||||
jio_fprintf(defaultStream::error_stream(),
|
||||
"An error has occurred while processing the"
|
||||
@ -90,7 +90,7 @@ static void fail(const char *msg, va_list ap) {
|
||||
void FileMapInfo::fail_stop(const char *msg, ...) {
|
||||
va_list ap;
|
||||
va_start(ap, msg);
|
||||
fail(msg, ap); // Never returns.
|
||||
fail_exit(msg, ap); // Never returns.
|
||||
va_end(ap); // for completeness.
|
||||
}
|
||||
|
||||
@ -118,7 +118,7 @@ void FileMapInfo::fail_continue(const char *msg, ...) {
|
||||
tty->print_cr("]");
|
||||
} else {
|
||||
if (RequireSharedSpaces) {
|
||||
fail(msg, ap);
|
||||
fail_exit(msg, ap);
|
||||
} else {
|
||||
if (log_is_enabled(Info, cds)) {
|
||||
ResourceMark rm;
|
||||
@ -252,13 +252,15 @@ void FileMapHeader::populate(FileMapInfo* mapinfo, size_t alignment) {
|
||||
_base_archive_is_default = false;
|
||||
}
|
||||
|
||||
void SharedClassPathEntry::init(const char* name, bool is_modules_image, TRAPS) {
|
||||
void SharedClassPathEntry::init(bool is_modules_image,
|
||||
ClassPathEntry* cpe, TRAPS) {
|
||||
assert(DumpSharedSpaces || DynamicDumpSharedSpaces, "dump time only");
|
||||
_timestamp = 0;
|
||||
_filesize = 0;
|
||||
_from_class_path_attr = false;
|
||||
|
||||
struct stat st;
|
||||
if (os::stat(name, &st) == 0) {
|
||||
if (os::stat(cpe->name(), &st) == 0) {
|
||||
if ((st.st_mode & S_IFMT) == S_IFDIR) {
|
||||
_type = dir_entry;
|
||||
} else {
|
||||
@ -268,6 +270,7 @@ void SharedClassPathEntry::init(const char* name, bool is_modules_image, TRAPS)
|
||||
} else {
|
||||
_type = jar_entry;
|
||||
_timestamp = st.st_mtime;
|
||||
_from_class_path_attr = cpe->from_class_path_attr();
|
||||
}
|
||||
_filesize = st.st_size;
|
||||
}
|
||||
@ -277,12 +280,12 @@ void SharedClassPathEntry::init(const char* name, bool is_modules_image, TRAPS)
|
||||
//
|
||||
// If we can't access a jar file in the boot path, then we can't
|
||||
// make assumptions about where classes get loaded from.
|
||||
FileMapInfo::fail_stop("Unable to open file %s.", name);
|
||||
FileMapInfo::fail_stop("Unable to open file %s.", cpe->name());
|
||||
}
|
||||
|
||||
size_t len = strlen(name) + 1;
|
||||
size_t len = strlen(cpe->name()) + 1;
|
||||
_name = MetadataFactory::new_array<char>(ClassLoaderData::the_null_class_loader_data(), (int)len, THREAD);
|
||||
strcpy(_name->data(), name);
|
||||
strcpy(_name->data(), cpe->name());
|
||||
}
|
||||
|
||||
bool SharedClassPathEntry::validate(bool is_class_path) {
|
||||
@ -381,7 +384,7 @@ void FileMapInfo::allocate_shared_path_table() {
|
||||
const char* type = (is_jrt ? "jrt" : (cpe->is_jar_file() ? "jar" : "dir"));
|
||||
log_info(class, path)("add main shared path (%s) %s", type, cpe->name());
|
||||
SharedClassPathEntry* ent = shared_path(i);
|
||||
ent->init(cpe->name(), is_jrt, THREAD);
|
||||
ent->init(is_jrt, cpe, THREAD);
|
||||
if (!is_jrt) { // No need to do the modules image.
|
||||
EXCEPTION_MARK; // The following call should never throw, but would exit VM on error.
|
||||
update_shared_classpath(cpe, ent, THREAD);
|
||||
@ -397,7 +400,7 @@ void FileMapInfo::allocate_shared_path_table() {
|
||||
while (acpe != NULL) {
|
||||
log_info(class, path)("add app shared path %s", acpe->name());
|
||||
SharedClassPathEntry* ent = shared_path(i);
|
||||
ent->init(acpe->name(), false, THREAD);
|
||||
ent->init(false, acpe, THREAD);
|
||||
EXCEPTION_MARK;
|
||||
update_shared_classpath(acpe, ent, THREAD);
|
||||
acpe = acpe->next();
|
||||
@ -409,7 +412,7 @@ void FileMapInfo::allocate_shared_path_table() {
|
||||
while (mpe != NULL) {
|
||||
log_info(class, path)("add module path %s",mpe->name());
|
||||
SharedClassPathEntry* ent = shared_path(i);
|
||||
ent->init(mpe->name(), false, THREAD);
|
||||
ent->init(false, mpe, THREAD);
|
||||
EXCEPTION_MARK;
|
||||
update_shared_classpath(mpe, ent, THREAD);
|
||||
mpe = mpe->next();
|
||||
@ -520,6 +523,182 @@ void FileMapInfo::update_shared_classpath(ClassPathEntry *cpe, SharedClassPathEn
|
||||
}
|
||||
}
|
||||
|
||||
char* FileMapInfo::skip_first_path_entry(const char* path) {
|
||||
size_t path_sep_len = strlen(os::path_separator());
|
||||
char* p = strstr((char*)path, os::path_separator());
|
||||
if (p != NULL) {
|
||||
debug_only( {
|
||||
size_t image_name_len = strlen(MODULES_IMAGE_NAME);
|
||||
assert(strncmp(p - image_name_len, MODULES_IMAGE_NAME, image_name_len) == 0,
|
||||
"first entry must be the modules image");
|
||||
} );
|
||||
p += path_sep_len;
|
||||
} else {
|
||||
debug_only( {
|
||||
assert(ClassLoader::string_ends_with(path, MODULES_IMAGE_NAME),
|
||||
"first entry must be the modules image");
|
||||
} );
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
int FileMapInfo::num_paths(const char* path) {
|
||||
if (path == NULL) {
|
||||
return 0;
|
||||
}
|
||||
int npaths = 1;
|
||||
char* p = (char*)path;
|
||||
while (p != NULL) {
|
||||
char* prev = p;
|
||||
p = strstr((char*)p, os::path_separator());
|
||||
if (p != NULL) {
|
||||
p++;
|
||||
// don't count empty path
|
||||
if ((p - prev) > 1) {
|
||||
npaths++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return npaths;
|
||||
}
|
||||
|
||||
GrowableArray<char*>* FileMapInfo::create_path_array(const char* path) {
|
||||
GrowableArray<char*>* path_array = new(ResourceObj::RESOURCE_AREA, mtInternal)
|
||||
GrowableArray<char*>(10);
|
||||
char* begin_ptr = (char*)path;
|
||||
char* end_ptr = strchr((char*)path, os::path_separator()[0]);
|
||||
if (end_ptr == NULL) {
|
||||
end_ptr = strchr((char*)path, '\0');
|
||||
}
|
||||
while (end_ptr != NULL) {
|
||||
if ((end_ptr - begin_ptr) > 1) {
|
||||
struct stat st;
|
||||
char* temp_name = NEW_RESOURCE_ARRAY(char, (size_t)(end_ptr - begin_ptr + 1));
|
||||
strncpy(temp_name, begin_ptr, end_ptr - begin_ptr);
|
||||
temp_name[end_ptr - begin_ptr] = '\0';
|
||||
if (os::stat(temp_name, &st) == 0) {
|
||||
path_array->append(temp_name);
|
||||
}
|
||||
}
|
||||
if (end_ptr < (path + strlen(path))) {
|
||||
begin_ptr = ++end_ptr;
|
||||
end_ptr = strchr(begin_ptr, os::path_separator()[0]);
|
||||
if (end_ptr == NULL) {
|
||||
end_ptr = strchr(begin_ptr, '\0');
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return path_array;
|
||||
}
|
||||
|
||||
bool FileMapInfo::fail(const char* msg, const char* name) {
|
||||
ClassLoader::trace_class_path(msg, name);
|
||||
MetaspaceShared::set_archive_loading_failed();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FileMapInfo::check_paths(int shared_path_start_idx, int num_paths, GrowableArray<char*>* rp_array) {
|
||||
int i = 0;
|
||||
int j = shared_path_start_idx;
|
||||
bool mismatch = false;
|
||||
while (i < num_paths && !mismatch) {
|
||||
while (shared_path(j)->from_class_path_attr()) {
|
||||
// shared_path(j) was expanded from the JAR file attribute "Class-Path:"
|
||||
// during dump time. It's not included in the -classpath VM argument.
|
||||
j++;
|
||||
}
|
||||
if (!os::same_files(shared_path(j)->name(), rp_array->at(i))) {
|
||||
mismatch = true;
|
||||
}
|
||||
i++;
|
||||
j++;
|
||||
}
|
||||
return mismatch;
|
||||
}
|
||||
|
||||
bool FileMapInfo::validate_boot_class_paths() {
|
||||
//
|
||||
// - Archive contains boot classes only - relaxed boot path check:
|
||||
// Extra path elements appended to the boot path at runtime are allowed.
|
||||
//
|
||||
// - Archive contains application or platform classes - strict boot path check:
|
||||
// Validate the entire runtime boot path, which must be compatible
|
||||
// with the dump time boot path. Appending boot path at runtime is not
|
||||
// allowed.
|
||||
//
|
||||
|
||||
// The first entry in boot path is the modules_image (guaranteed by
|
||||
// ClassLoader::setup_boot_search_path()). Skip the first entry. The
|
||||
// path of the runtime modules_image may be different from the dump
|
||||
// time path (e.g. the JDK image is copied to a different location
|
||||
// after generating the shared archive), which is acceptable. For most
|
||||
// common cases, the dump time boot path might contain modules_image only.
|
||||
char* runtime_boot_path = Arguments::get_sysclasspath();
|
||||
char* rp = skip_first_path_entry(runtime_boot_path);
|
||||
assert(shared_path(0)->is_modules_image(), "first shared_path must be the modules image");
|
||||
int dp_len = _header->_app_class_paths_start_index - 1; // ignore the first path to the module image
|
||||
bool mismatch = false;
|
||||
|
||||
bool relaxed_check = !header()->has_platform_or_app_classes();
|
||||
if (dp_len == 0 && rp == NULL) {
|
||||
return true; // ok, both runtime and dump time boot paths have modules_images only
|
||||
} else if (dp_len == 0 && rp != NULL) {
|
||||
if (relaxed_check) {
|
||||
return true; // ok, relaxed check, runtime has extra boot append path entries
|
||||
} else {
|
||||
mismatch = true;
|
||||
}
|
||||
} else if (dp_len > 0 && rp != NULL) {
|
||||
int num;
|
||||
ResourceMark rm;
|
||||
GrowableArray<char*>* rp_array = create_path_array(rp);
|
||||
int rp_len = rp_array->length();
|
||||
if (rp_len >= dp_len) {
|
||||
if (relaxed_check) {
|
||||
// only check the leading entries in the runtime boot path, up to
|
||||
// the length of the dump time boot path
|
||||
num = dp_len;
|
||||
} else {
|
||||
// check the full runtime boot path, must match with dump time
|
||||
num = rp_len;
|
||||
}
|
||||
mismatch = check_paths(1, num, rp_array);
|
||||
}
|
||||
}
|
||||
|
||||
if (mismatch) {
|
||||
// The paths are different
|
||||
return fail("[BOOT classpath mismatch, actual =", runtime_boot_path);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FileMapInfo::validate_app_class_paths(int shared_app_paths_len) {
|
||||
const char *appcp = Arguments::get_appclasspath();
|
||||
assert(appcp != NULL, "NULL app classpath");
|
||||
int rp_len = num_paths(appcp);
|
||||
bool mismatch = false;
|
||||
if (rp_len < shared_app_paths_len) {
|
||||
return fail("Run time APP classpath is shorter than the one at dump time: ", appcp);
|
||||
}
|
||||
if (shared_app_paths_len != 0 && rp_len != 0) {
|
||||
// Prefix is OK: E.g., dump with -cp foo.jar, but run with -cp foo.jar:bar.jar.
|
||||
ResourceMark rm;
|
||||
GrowableArray<char*>* rp_array = create_path_array(appcp);
|
||||
if (rp_array->length() == 0) {
|
||||
// None of the jar file specified in the runtime -cp exists.
|
||||
return fail("None of the jar file specified in the runtime -cp exists: -Djava.class.path=", appcp);
|
||||
}
|
||||
int j = _header->_app_class_paths_start_index;
|
||||
mismatch = check_paths(j, shared_app_paths_len, rp_array);
|
||||
if (mismatch) {
|
||||
return fail("[APP classpath mismatch, actual: -Djava.class.path=", appcp);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FileMapInfo::validate_shared_path_table() {
|
||||
assert(UseSharedSpaces, "runtime only");
|
||||
@ -550,11 +729,16 @@ bool FileMapInfo::validate_shared_path_table() {
|
||||
}
|
||||
|
||||
int module_paths_start_index = _header->_app_module_paths_start_index;
|
||||
int shared_app_paths_len = 0;
|
||||
|
||||
// validate the path entries up to the _max_used_path_index
|
||||
for (int i=0; i < _header->_max_used_path_index + 1; i++) {
|
||||
if (i < module_paths_start_index) {
|
||||
if (shared_path(i)->validate()) {
|
||||
// Only count the app class paths not from the "Class-path" attribute of a jar manifest.
|
||||
if (!shared_path(i)->from_class_path_attr() && i >= _header->_app_class_paths_start_index) {
|
||||
shared_app_paths_len++;
|
||||
}
|
||||
log_info(class, path)("ok");
|
||||
} else {
|
||||
if (_dynamic_archive_info != NULL && _dynamic_archive_info->_is_static) {
|
||||
@ -574,6 +758,16 @@ bool FileMapInfo::validate_shared_path_table() {
|
||||
}
|
||||
}
|
||||
|
||||
if (_header->_max_used_path_index == 0) {
|
||||
// default archive only contains the module image in the bootclasspath
|
||||
assert(shared_path(0)->is_modules_image(), "first shared_path must be the modules image");
|
||||
} else {
|
||||
if (!validate_boot_class_paths() || !validate_app_class_paths(shared_app_paths_len)) {
|
||||
fail_continue("shared class paths mismatch (hint: enable -Xlog:class+path=info to diagnose the failure)");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
_validating_shared_path_table = false;
|
||||
|
||||
#if INCLUDE_JVMTI
|
||||
@ -588,39 +782,6 @@ bool FileMapInfo::validate_shared_path_table() {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FileMapInfo::same_files(const char* file1, const char* file2) {
|
||||
if (strcmp(file1, file2) == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool is_same = false;
|
||||
// if the two paths diff only in case
|
||||
struct stat st1;
|
||||
struct stat st2;
|
||||
int ret1;
|
||||
int ret2;
|
||||
ret1 = os::stat(file1, &st1);
|
||||
ret2 = os::stat(file2, &st2);
|
||||
if (ret1 < 0 || ret2 < 0) {
|
||||
// one of the files is invalid. So they are not the same.
|
||||
is_same = false;
|
||||
} else if (st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino) {
|
||||
// different files
|
||||
is_same = false;
|
||||
#ifndef _WINDOWS
|
||||
} else if (st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino) {
|
||||
// same files
|
||||
is_same = true;
|
||||
#else
|
||||
} else if ((st1.st_size == st2.st_size) && (st1.st_ctime == st2.st_ctime) &&
|
||||
(st1.st_mtime == st2.st_mtime)) {
|
||||
// same files
|
||||
is_same = true;
|
||||
#endif
|
||||
}
|
||||
return is_same;
|
||||
}
|
||||
|
||||
bool FileMapInfo::check_archive(const char* archive_name, bool is_static) {
|
||||
int fd = os::open(archive_name, O_RDONLY | O_BINARY, 0);
|
||||
if (fd < 0) {
|
||||
@ -1749,7 +1910,7 @@ ClassPathEntry* FileMapInfo::get_classpath_entry_for_jvmti(int i, TRAPS) {
|
||||
jio_snprintf(msg, strlen(path) + 127, "error in opening JAR file %s", path);
|
||||
THROW_MSG_(vmSymbols::java_io_IOException(), msg, NULL);
|
||||
} else {
|
||||
ent = ClassLoader::create_class_path_entry(path, &st, /*throw_exception=*/true, false, CHECK_NULL);
|
||||
ent = ClassLoader::create_class_path_entry(path, &st, /*throw_exception=*/true, false, false, CHECK_NULL);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -53,13 +53,14 @@ class SharedClassPathEntry {
|
||||
};
|
||||
protected:
|
||||
u1 _type;
|
||||
bool _from_class_path_attr;
|
||||
time_t _timestamp; // jar timestamp, 0 if is directory, modules image or other
|
||||
long _filesize; // jar/jimage file size, -1 if is directory, -2 if other
|
||||
Array<char>* _name;
|
||||
Array<u1>* _manifest;
|
||||
|
||||
public:
|
||||
void init(const char* name, bool is_modules_image, TRAPS);
|
||||
void init(bool is_modules_image, ClassPathEntry* cpe, TRAPS);
|
||||
void metaspace_pointers_do(MetaspaceClosure* it);
|
||||
bool validate(bool is_class_path = true);
|
||||
|
||||
@ -74,6 +75,7 @@ public:
|
||||
void set_is_signed() {
|
||||
_type = signed_jar_entry;
|
||||
}
|
||||
bool from_class_path_attr() { return _from_class_path_attr; }
|
||||
time_t timestamp() const { return _timestamp; }
|
||||
long filesize() const { return _filesize; }
|
||||
const char* name() const { return _name->data(); }
|
||||
@ -240,7 +242,6 @@ public:
|
||||
static bool get_base_archive_name_from_header(const char* archive_name,
|
||||
int* size, char** base_archive_name);
|
||||
static bool check_archive(const char* archive_name, bool is_static);
|
||||
static bool same_files(const char* file1, const char* file2);
|
||||
void restore_shared_path_table();
|
||||
bool init_from_file(int fd, bool is_static);
|
||||
static void metaspace_pointers_do(MetaspaceClosure* it);
|
||||
@ -376,6 +377,15 @@ public:
|
||||
char* region_addr(int idx);
|
||||
|
||||
private:
|
||||
char* skip_first_path_entry(const char* path) NOT_CDS_RETURN_(NULL);
|
||||
int num_paths(const char* path) NOT_CDS_RETURN_(0);
|
||||
GrowableArray<char*>* create_path_array(const char* path) NOT_CDS_RETURN_(NULL);
|
||||
bool fail(const char* msg, const char* name) NOT_CDS_RETURN_(FALSE);
|
||||
bool check_paths(int shared_path_start_idx,
|
||||
int num_paths,
|
||||
GrowableArray<char*>* rp_array) NOT_CDS_RETURN_(FALSE);
|
||||
bool validate_boot_class_paths() NOT_CDS_RETURN_(FALSE);
|
||||
bool validate_app_class_paths(int shared_app_paths_len) NOT_CDS_RETURN_(FALSE);
|
||||
bool map_heap_data(MemRegion **heap_mem, int first, int max, int* num,
|
||||
bool is_open = false) NOT_CDS_JAVA_HEAP_RETURN_(false);
|
||||
bool region_crc_check(char* buf, size_t size, int expected_crc) NOT_CDS_RETURN_(false);
|
||||
|
||||
@ -3571,7 +3571,7 @@ bool Arguments::init_shared_archive_paths() {
|
||||
"Cannot have more than 1 archive file specified in -XX:SharedArchiveFile during CDS dumping");
|
||||
}
|
||||
if (DynamicDumpSharedSpaces) {
|
||||
if (FileMapInfo::same_files(SharedArchiveFile, ArchiveClassesAtExit)) {
|
||||
if (os::same_files(SharedArchiveFile, ArchiveClassesAtExit)) {
|
||||
vm_exit_during_initialization(
|
||||
"Cannot have the same archive file specified for -XX:SharedArchiveFile and -XX:ArchiveClassesAtExit",
|
||||
SharedArchiveFile);
|
||||
|
||||
@ -171,10 +171,6 @@ class os: AllStatic {
|
||||
init_globals_ext();
|
||||
}
|
||||
|
||||
// File names are case-insensitive on windows only
|
||||
// Override me as needed
|
||||
static int file_name_strncmp(const char* s1, const char* s2, size_t num);
|
||||
|
||||
// unset environment variable
|
||||
static bool unsetenv(const char* name);
|
||||
|
||||
@ -544,6 +540,8 @@ class os: AllStatic {
|
||||
|
||||
static int compare_file_modified_times(const char* file1, const char* file2);
|
||||
|
||||
static bool same_files(const char* file1, const char* file2);
|
||||
|
||||
//File i/o operations
|
||||
|
||||
static ssize_t read(int fd, void *buf, unsigned int nBytes);
|
||||
|
||||
@ -328,6 +328,7 @@ hotspot_appcds_dynamic = \
|
||||
-runtime/appcds/ExtraSymbols.java \
|
||||
-runtime/appcds/LongClassListPath.java \
|
||||
-runtime/appcds/LotsOfClasses.java \
|
||||
-runtime/appcds/RelativePath.java \
|
||||
-runtime/appcds/SharedArchiveConsistency.java \
|
||||
-runtime/appcds/UnusedCPDuringDump.java \
|
||||
-runtime/appcds/VerifierTest_1B.java
|
||||
|
||||
@ -28,9 +28,15 @@ import jdk.test.lib.cds.CDSOptions;
|
||||
|
||||
public class AppCDSOptions extends CDSOptions {
|
||||
public String appJar;
|
||||
public String appJarDir;
|
||||
|
||||
public AppCDSOptions setAppJar(String appJar) {
|
||||
this.appJar = appJar;
|
||||
return this;
|
||||
}
|
||||
|
||||
public AppCDSOptions setAppJarDir(String appJarDir) {
|
||||
this.appJarDir = appJarDir;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
@ -36,6 +36,9 @@
|
||||
*/
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import jdk.test.lib.process.OutputAnalyzer;
|
||||
|
||||
public class AppendClasspath {
|
||||
@ -53,9 +56,24 @@ public class AppendClasspath {
|
||||
"HelloMore")
|
||||
.assertNormalExit();
|
||||
|
||||
// PASS: 2) runtime has an non-existing jar in the -cp
|
||||
String classDir = System.getProperty("test.classes");
|
||||
String newFile = "non-exist.jar";
|
||||
String nonExistPath = classDir + File.separator + newFile;
|
||||
String classPath = appJar + File.pathSeparator + nonExistPath;
|
||||
File nonExistJar = new File(classDir, newFile);
|
||||
if (nonExistJar.exists()) {
|
||||
nonExistJar.delete();
|
||||
}
|
||||
TestCommon.run(
|
||||
"-cp", classPath,
|
||||
"-Xlog:class+path=trace",
|
||||
"Hello")
|
||||
.assertNormalExit();
|
||||
|
||||
final String errorMessage1 = "Unable to use shared archive";
|
||||
final String errorMessage2 = "shared class paths mismatch";
|
||||
// FAIL: 2) runtime with classpath different from the one used in dump time
|
||||
// FAIL: 1) runtime with classpath different from the one used in dump time
|
||||
// (runtime has an extra jar file prepended to the class path)
|
||||
TestCommon.run(
|
||||
"-Xlog:cds",
|
||||
@ -63,7 +81,7 @@ public class AppendClasspath {
|
||||
"HelloMore")
|
||||
.assertAbnormalExit(errorMessage1, errorMessage2);
|
||||
|
||||
// FAIL: 3) runtime with classpath part of the one used in dump time
|
||||
// FAIL: 2) runtime with classpath part of the one used in dump time
|
||||
TestCommon.testDump(appJar + File.pathSeparator + appJar2,
|
||||
TestCommon.list("Hello"));
|
||||
TestCommon.run(
|
||||
@ -72,12 +90,26 @@ public class AppendClasspath {
|
||||
"Hello")
|
||||
.assertAbnormalExit(errorMessage1, errorMessage2);
|
||||
|
||||
// FAIL: 4) runtime with same set of jar files in the classpath but
|
||||
// FAIL: 3) runtime with same set of jar files in the classpath but
|
||||
// with different order
|
||||
TestCommon.run(
|
||||
"-Xlog:cds",
|
||||
"-cp", appJar2 + File.pathSeparator + appJar,
|
||||
"HelloMore")
|
||||
.assertAbnormalExit(errorMessage1, errorMessage2);
|
||||
}
|
||||
|
||||
// FAIL: 4) non-existing jar during dump time but jar exists during runtime
|
||||
TestCommon.testDump(classPath, TestCommon.list("Hello"));
|
||||
|
||||
Files.copy(Paths.get(classDir, "hello.jar"),
|
||||
Paths.get(classDir, newFile),
|
||||
StandardCopyOption.REPLACE_EXISTING);
|
||||
|
||||
TestCommon.run(
|
||||
"-cp", classPath,
|
||||
"-Xlog:class+path=trace",
|
||||
"Hello")
|
||||
.assertAbnormalExit(errorMessage1, errorMessage2);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -34,6 +34,7 @@
|
||||
* @run driver BootClassPathMismatch
|
||||
*/
|
||||
|
||||
import jdk.test.lib.Platform;
|
||||
import jdk.test.lib.cds.CDSOptions;
|
||||
import jdk.test.lib.cds.CDSTestUtils;
|
||||
import jdk.test.lib.process.OutputAnalyzer;
|
||||
@ -41,7 +42,9 @@ import java.io.File;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.FileAlreadyExistsException;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.attribute.FileTime;
|
||||
|
||||
|
||||
public class BootClassPathMismatch {
|
||||
@ -126,6 +129,55 @@ public class BootClassPathMismatch {
|
||||
"-cp", appJar, "-verbose:class",
|
||||
"-Xbootclasspath/a:" + appJar, "Hello")
|
||||
.assertNormalExit("[class,load] Hello source: shared objects file");
|
||||
|
||||
// test relative path to appJar
|
||||
String newJar = TestCommon.composeRelPath(appJar);
|
||||
TestCommon.run(
|
||||
"-cp", newJar, "-verbose:class",
|
||||
"-Xbootclasspath/a:" + newJar, "Hello")
|
||||
.assertNormalExit("[class,load] Hello source: shared objects file");
|
||||
|
||||
int idx = appJar.lastIndexOf(File.separator);
|
||||
String jarName = appJar.substring(idx + 1);
|
||||
String jarDir = appJar.substring(0, idx);
|
||||
// relative path starting with "."
|
||||
TestCommon.runWithRelativePath(
|
||||
jarDir,
|
||||
"-Xshare:on",
|
||||
"-XX:SharedArchiveFile=" + TestCommon.getCurrentArchiveName(),
|
||||
"-cp", "." + File.separator + jarName,
|
||||
"-Xbootclasspath/a:" + "." + File.separator + jarName,
|
||||
"-Xlog:class+load=trace,class+path=info",
|
||||
"Hello")
|
||||
.assertNormalExit(output -> {
|
||||
output.shouldContain("Hello source: shared objects file")
|
||||
.shouldHaveExitValue(0);
|
||||
});
|
||||
|
||||
// relative path starting with ".."
|
||||
idx = jarDir.lastIndexOf(File.separator);
|
||||
String jarSubDir = jarDir.substring(idx + 1);
|
||||
TestCommon.runWithRelativePath(
|
||||
jarDir,
|
||||
"-Xshare:on",
|
||||
"-XX:SharedArchiveFile=" + TestCommon.getCurrentArchiveName(),
|
||||
"-cp", ".." + File.separator + jarSubDir + File.separator + jarName,
|
||||
"-Xbootclasspath/a:" + ".." + File.separator + jarSubDir + File.separator + jarName,
|
||||
"-Xlog:class+load=trace,class+path=info",
|
||||
"Hello")
|
||||
.assertNormalExit(output -> {
|
||||
output.shouldContain("Hello source: shared objects file")
|
||||
.shouldHaveExitValue(0);
|
||||
});
|
||||
|
||||
// test sym link to appJar
|
||||
if (!Platform.isWindows()) {
|
||||
File linkedJar = TestCommon.createSymLink(appJar);
|
||||
TestCommon.run(
|
||||
"-cp", linkedJar.getPath(), "-verbose:class",
|
||||
"-Xbootclasspath/a:" + linkedJar.getPath(), "Hello")
|
||||
.assertNormalExit("[class,load] Hello source: shared objects file");
|
||||
}
|
||||
}
|
||||
|
||||
/* Archive contains boot classes only, runtime add -Xbootclasspath/a path.
|
||||
@ -158,6 +210,12 @@ public class BootClassPathMismatch {
|
||||
"-Xlog:cds",
|
||||
"-cp", appJar, "-Xbootclasspath/a:" + appJar, "Hello")
|
||||
.assertAbnormalExit(mismatchMessage);
|
||||
|
||||
// test relative path to appJar
|
||||
String newJar = TestCommon.composeRelPath(appJar);
|
||||
TestCommon.run(
|
||||
"-cp", newJar, "-Xbootclasspath/a:" + newJar, "Hello")
|
||||
.assertAbnormalExit(mismatchMessage);
|
||||
}
|
||||
|
||||
private static void copyHelloToNewDir() throws Exception {
|
||||
@ -168,13 +226,25 @@ public class BootClassPathMismatch {
|
||||
} catch (FileAlreadyExistsException e) { }
|
||||
|
||||
// copy as hello.jar
|
||||
Path dstPath = Paths.get(dstDir, "hello.jar");
|
||||
Files.copy(Paths.get(classDir, "hello.jar"),
|
||||
Paths.get(dstDir, "hello.jar"),
|
||||
dstPath,
|
||||
StandardCopyOption.REPLACE_EXISTING);
|
||||
|
||||
File helloJar = dstPath.toFile();
|
||||
long modTime = helloJar.lastModified();
|
||||
|
||||
// copy as hello.jar1
|
||||
Path dstPath2 = Paths.get(dstDir, "hello.jar1");
|
||||
Files.copy(Paths.get(classDir, "hello.jar"),
|
||||
Paths.get(dstDir, "hello.jar1"),
|
||||
dstPath2,
|
||||
StandardCopyOption.REPLACE_EXISTING);
|
||||
|
||||
// On Windows, we rely on the file size, creation time, and
|
||||
// modification time in order to differentiate between 2 files.
|
||||
// Setting a different modification time on hello.jar1 so that this test
|
||||
// runs more reliably on Windows.
|
||||
modTime += 10000;
|
||||
Files.setAttribute(dstPath2, "lastModifiedTime", FileTime.fromMillis(modTime));
|
||||
}
|
||||
}
|
||||
|
||||
@ -105,12 +105,13 @@ public class PrintSharedArchiveAndExit {
|
||||
"-XX:+PrintSharedArchiveAndExit")
|
||||
.ifNoMappingFailure(output -> check(output, 1, true, lastCheckMsg, "Run time APP classpath is shorter than the one at dump time: ."));
|
||||
|
||||
log("Use an invalid App CP -- all the JAR paths should be checked");
|
||||
log("Use an invalid App CP -- all the JAR paths should be checked.\n" +
|
||||
"Non-existing jar files will be ignored.");
|
||||
String invalidCP = "non-existing-dir" + File.pathSeparator + cp;
|
||||
TestCommon.run(
|
||||
"-cp", invalidCP,
|
||||
"-XX:+PrintSharedArchiveAndExit")
|
||||
.ifNoMappingFailure(output -> check(output, 1, true, lastCheckMsg, "APP classpath mismatch, actual: -Djava.class.path=" + invalidCP));
|
||||
.ifNoMappingFailure(output -> check(output, 0, true, lastCheckMsg));
|
||||
|
||||
log("Changed modification time of hello.jar -- all the JAR paths should be checked");
|
||||
(new File(appJar)).setLastModified(System.currentTimeMillis() + 2000);
|
||||
|
||||
173
test/hotspot/jtreg/runtime/appcds/RelativePath.java
Normal file
173
test/hotspot/jtreg/runtime/appcds/RelativePath.java
Normal file
@ -0,0 +1,173 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @summary Test relative paths specified in the -cp.
|
||||
* @requires vm.cds
|
||||
* @library /test/lib
|
||||
* @modules java.base/jdk.internal.misc
|
||||
* java.management
|
||||
* jdk.jartool/sun.tools.jar
|
||||
* @compile test-classes/Hello.java
|
||||
* @compile test-classes/HelloMore.java
|
||||
* @run driver RelativePath
|
||||
*/
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import static java.nio.file.StandardCopyOption.COPY_ATTRIBUTES;
|
||||
import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
|
||||
import java.util.Arrays;
|
||||
import jdk.test.lib.Platform;
|
||||
|
||||
public class RelativePath {
|
||||
|
||||
private static final Path USER_DIR = Paths.get(System.getProperty("user.dir"));
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
String appJar = JarBuilder.getOrCreateHelloJar();
|
||||
String appJar2 = JarBuilder.build("AppendClasspath_HelloMore", "HelloMore");
|
||||
|
||||
// dump an archive with only the jar name in the -cp
|
||||
int idx = appJar.lastIndexOf(File.separator);
|
||||
String jarName = appJar.substring(idx + 1);
|
||||
String jarDir = appJar.substring(0, idx);
|
||||
TestCommon.testDump(jarDir, jarName, TestCommon.list("Hello"));
|
||||
|
||||
// copy the jar file to another dir. Specify the jar file without
|
||||
// a directory path.
|
||||
Path srcPath = Paths.get(appJar);
|
||||
Path destDir = Files.createTempDirectory(USER_DIR, "deploy");
|
||||
Path destPath = destDir.resolve(jarName);
|
||||
Files.copy(srcPath, destPath, REPLACE_EXISTING, COPY_ATTRIBUTES);
|
||||
TestCommon.runWithRelativePath(
|
||||
destDir.toString(),
|
||||
"-Xshare:on",
|
||||
"-XX:SharedArchiveFile=" + TestCommon.getCurrentArchiveName(),
|
||||
"-cp", jarName + File.pathSeparator + appJar2,
|
||||
"-Xlog:class+load=trace,class+path=info",
|
||||
"HelloMore")
|
||||
.assertNormalExit(output -> {
|
||||
output.shouldContain("Hello source: shared objects file")
|
||||
.shouldHaveExitValue(0);
|
||||
});
|
||||
|
||||
// Long path test
|
||||
// Create a long directory path and copy the appJar there.
|
||||
final int MAX_PATH = 260;
|
||||
destDir = Paths.get(jarDir);
|
||||
int subDirLen = MAX_PATH - jarDir.length() - 3;
|
||||
if (subDirLen > 0) {
|
||||
char[] chars = new char[subDirLen];
|
||||
Arrays.fill(chars, 'x');
|
||||
String subPath = new String(chars);
|
||||
destDir = Paths.get(jarDir, subPath);
|
||||
}
|
||||
File longDir = destDir.toFile();
|
||||
longDir.mkdir();
|
||||
String destJar = longDir.getPath() + File.separator + jarName;
|
||||
Files.copy(Paths.get(appJar), Paths.get(destJar), REPLACE_EXISTING);
|
||||
// Create an archive with the appJar in the long directory path.
|
||||
TestCommon.testDump(destJar, TestCommon.list("Hello"));
|
||||
|
||||
// Run with -cp containing the appJar and another jar appended.
|
||||
TestCommon.run(
|
||||
"-cp", destJar + File.pathSeparator + appJar2,
|
||||
"-Xlog:class+load=trace,class+path=info",
|
||||
"HelloMore")
|
||||
.assertNormalExit(output -> {
|
||||
output.shouldContain("Hello source: shared objects file")
|
||||
.shouldHaveExitValue(0);
|
||||
});
|
||||
|
||||
// Dump an archive with a specified JAR file in -classpath
|
||||
TestCommon.testDump(appJar, TestCommon.list("Hello"));
|
||||
|
||||
// compose a relative path to the hello.jar
|
||||
String newHello = TestCommon.composeRelPath(appJar);
|
||||
|
||||
// create a sym link to the original hello.jar
|
||||
File linkedHello = null;
|
||||
if (!Platform.isWindows()) {
|
||||
linkedHello = TestCommon.createSymLink(appJar);
|
||||
}
|
||||
|
||||
// PASS:1) same appJar but referred to via a relative path
|
||||
TestCommon.run(
|
||||
"-cp", newHello + File.pathSeparator + appJar2,
|
||||
"-Xlog:class+load=trace,class+path=info",
|
||||
"HelloMore")
|
||||
.assertNormalExit();
|
||||
|
||||
// PASS:2) relative path starting with "."
|
||||
TestCommon.runWithRelativePath(
|
||||
jarDir,
|
||||
"-Xshare:on",
|
||||
"-XX:SharedArchiveFile=" + TestCommon.getCurrentArchiveName(),
|
||||
"-cp", "." + File.separator + jarName + File.pathSeparator + appJar2,
|
||||
"-Xlog:class+load=trace,class+path=info",
|
||||
"HelloMore")
|
||||
.assertNormalExit(output -> {
|
||||
output.shouldContain("Hello source: shared objects file")
|
||||
.shouldHaveExitValue(0);
|
||||
});
|
||||
|
||||
// PASS:3) relative path starting with ".."
|
||||
idx = jarDir.lastIndexOf(File.separator);
|
||||
String jarSubDir = jarDir.substring(idx + 1);
|
||||
TestCommon.runWithRelativePath(
|
||||
jarDir,
|
||||
"-Xshare:on",
|
||||
"-XX:SharedArchiveFile=" + TestCommon.getCurrentArchiveName(),
|
||||
"-cp", ".." + File.separator + jarSubDir + File.separator + jarName
|
||||
+ File.pathSeparator + appJar2,
|
||||
"-Xlog:class+load=trace,class+path=info",
|
||||
"HelloMore")
|
||||
.assertNormalExit(output -> {
|
||||
output.shouldContain("Hello source: shared objects file")
|
||||
.shouldHaveExitValue(0);
|
||||
});
|
||||
|
||||
// PASS:4) a jar linked to the original hello.jar
|
||||
if (!Platform.isWindows()) {
|
||||
TestCommon.run(
|
||||
"-cp", linkedHello.getPath() + File.pathSeparator + appJar2,
|
||||
"HelloMore")
|
||||
.assertNormalExit();
|
||||
}
|
||||
|
||||
final String errorMessage1 = "Unable to use shared archive";
|
||||
final String errorMessage2 = "shared class paths mismatch";
|
||||
// FAIL: 1) runtime with classpath different from the one used in dump time
|
||||
// (runtime has an extra jar file prepended to the class path)
|
||||
TestCommon.run(
|
||||
"-cp", appJar2 + File.pathSeparator + newHello,
|
||||
"HelloMore")
|
||||
.assertAbnormalExit(errorMessage1, errorMessage2);
|
||||
|
||||
}
|
||||
}
|
||||
@ -128,6 +128,10 @@ public class TestCommon extends CDSTestUtils {
|
||||
return createArchive(appJar, classList, suffix);
|
||||
}
|
||||
|
||||
public static OutputAnalyzer dump(String appJarDir, String appJar, String classList[],
|
||||
String... suffix) throws Exception {
|
||||
return createArchive(appJarDir, appJar, classList, suffix);
|
||||
}
|
||||
|
||||
// Create AppCDS archive using most common args - convenience method
|
||||
public static OutputAnalyzer createArchive(String appJar, String classList[],
|
||||
@ -138,6 +142,15 @@ public class TestCommon extends CDSTestUtils {
|
||||
return createArchive(opts);
|
||||
}
|
||||
|
||||
public static OutputAnalyzer createArchive(String appJarDir, String appJar, String classList[],
|
||||
String... suffix) throws Exception {
|
||||
AppCDSOptions opts = (new AppCDSOptions()).setAppJar(appJar);
|
||||
opts.setAppJarDir(appJarDir);
|
||||
opts.setClassList(classList);
|
||||
opts.addSuffix(suffix);
|
||||
return createArchive(opts);
|
||||
}
|
||||
|
||||
// Simulate -Xshare:dump with -XX:ArchiveClassesAtExit. See comments around patchJarForDynamicDump()
|
||||
private static final Class tmp = DynamicDumpHelper.class;
|
||||
|
||||
@ -222,6 +235,9 @@ public class TestCommon extends CDSTestUtils {
|
||||
|
||||
String[] cmdLine = cmd.toArray(new String[cmd.size()]);
|
||||
ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(true, cmdLine);
|
||||
if (opts.appJarDir != null) {
|
||||
pb.directory(new File(opts.appJarDir));
|
||||
}
|
||||
return executeAndLog(pb, "dump");
|
||||
}
|
||||
|
||||
@ -360,6 +376,9 @@ public class TestCommon extends CDSTestUtils {
|
||||
|
||||
String[] cmdLine = cmd.toArray(new String[cmd.size()]);
|
||||
ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(true, cmdLine);
|
||||
if (opts.appJarDir != null) {
|
||||
pb.directory(new File(opts.appJarDir));
|
||||
}
|
||||
return executeAndLog(pb, "exec");
|
||||
}
|
||||
|
||||
@ -378,6 +397,13 @@ public class TestCommon extends CDSTestUtils {
|
||||
return new Result(opts, runWithArchive(opts));
|
||||
}
|
||||
|
||||
public static Result runWithRelativePath(String jarDir, String... suffix) throws Exception {
|
||||
AppCDSOptions opts = (new AppCDSOptions());
|
||||
opts.setAppJarDir(jarDir);
|
||||
opts.addSuffix(suffix);
|
||||
return new Result(opts, runWithArchive(opts));
|
||||
}
|
||||
|
||||
public static OutputAnalyzer exec(String appJar, String... suffix) throws Exception {
|
||||
AppCDSOptions opts = (new AppCDSOptions()).setAppJar(appJar);
|
||||
opts.addSuffix(suffix);
|
||||
@ -443,6 +469,20 @@ public class TestCommon extends CDSTestUtils {
|
||||
return output;
|
||||
}
|
||||
|
||||
public static OutputAnalyzer testDump(String appJarDir, String appJar, String classList[],
|
||||
String... suffix) throws Exception {
|
||||
OutputAnalyzer output = dump(appJarDir, appJar, classList, suffix);
|
||||
if (DYNAMIC_DUMP) {
|
||||
if (isUnableToMap(output)) {
|
||||
throw new SkippedException(UnableToMapMsg);
|
||||
}
|
||||
output.shouldContain("Written dynamic archive");
|
||||
} else {
|
||||
output.shouldContain("Loading classes to share");
|
||||
}
|
||||
output.shouldHaveExitValue(0);
|
||||
return output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple test -- dump and execute appJar with the given classList in classlist.
|
||||
@ -590,4 +630,32 @@ public class TestCommon extends CDSTestUtils {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static String composeRelPath(String appJar) {
|
||||
int idx = appJar.lastIndexOf(File.separator);
|
||||
String jarName = appJar.substring(idx + 1);
|
||||
String jarDir = appJar.substring(0, idx);
|
||||
String lastDir = jarDir.substring(jarDir.lastIndexOf(File.separator));
|
||||
String relPath = jarDir + File.separator + ".." + File.separator + lastDir;
|
||||
String newJar = relPath + File.separator + jarName;
|
||||
return newJar;
|
||||
}
|
||||
|
||||
|
||||
public static File createSymLink(String appJar) throws Exception {
|
||||
int idx = appJar.lastIndexOf(File.separator);
|
||||
String jarName = appJar.substring(idx + 1);
|
||||
String jarDir = appJar.substring(0, idx);
|
||||
File origJar = new File(jarDir, jarName);
|
||||
String linkedJarName = "linked_" + jarName;
|
||||
File linkedJar = null;
|
||||
if (!Platform.isWindows()) {
|
||||
linkedJar = new File(jarDir, linkedJarName);
|
||||
if (linkedJar.exists()) {
|
||||
linkedJar.delete();
|
||||
}
|
||||
Files.createSymbolicLink(linkedJar.toPath(), origJar.toPath());
|
||||
}
|
||||
return linkedJar;
|
||||
}
|
||||
}
|
||||
|
||||
@ -108,7 +108,7 @@ class DynamicArchiveTestBase {
|
||||
cmdLine = TestCommon.concat(cmdLine, "-XX:SharedArchiveFile=" + baseArchiveName);
|
||||
}
|
||||
cmdLine = TestCommon.concat(cmdLine, cmdLineSuffix);
|
||||
return execProcess("dump", cmdLine);
|
||||
return execProcess("dump", null, cmdLine);
|
||||
}
|
||||
|
||||
public static Result dump2_WB(String baseArchiveName, String topArchiveName, String ... cmdLineSuffix)
|
||||
@ -188,7 +188,23 @@ class DynamicArchiveTestBase {
|
||||
"-Xshare:on",
|
||||
"-XX:SharedArchiveFile=" + archiveFiles);
|
||||
cmdLine = TestCommon.concat(cmdLine, cmdLineSuffix);
|
||||
return execProcess("exec", cmdLine);
|
||||
return execProcess("exec", null, cmdLine);
|
||||
}
|
||||
|
||||
public static Result runWithRelativePath(String baseArchiveName, String topArchiveName,
|
||||
String jarDir, String ... cmdLineSuffix)
|
||||
throws Exception {
|
||||
if (baseArchiveName == null && topArchiveName == null) {
|
||||
throw new RuntimeException("Both baseArchiveName and topArchiveName cannot be null at the same time.");
|
||||
}
|
||||
String archiveFiles = (baseArchiveName == null) ? topArchiveName :
|
||||
(topArchiveName == null) ? baseArchiveName :
|
||||
baseArchiveName + File.pathSeparator + topArchiveName;
|
||||
String[] cmdLine = TestCommon.concat(
|
||||
"-Xshare:on",
|
||||
"-XX:SharedArchiveFile=" + archiveFiles);
|
||||
cmdLine = TestCommon.concat(cmdLine, cmdLineSuffix);
|
||||
return execProcess("exec", jarDir, cmdLine);
|
||||
}
|
||||
|
||||
public static Result run2_WB(String baseArchiveName, String topArchiveName, String ... cmdLineSuffix)
|
||||
@ -221,11 +237,14 @@ class DynamicArchiveTestBase {
|
||||
}
|
||||
|
||||
|
||||
private static Result execProcess(String mode, String[] cmdLine) throws Exception {
|
||||
private static Result execProcess(String mode, String jarDir, String[] cmdLine) throws Exception {
|
||||
if (!executedIn_run) {
|
||||
throw new Exception("Test error: dynamic archive tests must be executed via DynamicArchiveTestBase.run()");
|
||||
}
|
||||
ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(true, cmdLine);
|
||||
if (jarDir != null) {
|
||||
pb.directory(new File(jarDir));
|
||||
}
|
||||
OutputAnalyzer output = TestCommon.executeAndLog(pb, mode);
|
||||
CDSOptions opts = new CDSOptions();
|
||||
String xShareMode = getXshareMode(cmdLine);
|
||||
|
||||
@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @summary Test relative paths specified in the -cp.
|
||||
* @requires vm.cds
|
||||
* @library /test/lib /test/hotspot/jtreg/runtime/appcds
|
||||
* @modules java.base/jdk.internal.misc
|
||||
* java.management
|
||||
* jdk.jartool/sun.tools.jar
|
||||
* @compile ../test-classes/Hello.java
|
||||
* @compile ../test-classes/HelloMore.java
|
||||
* @run driver RelativePath
|
||||
*/
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public class RelativePath extends DynamicArchiveTestBase {
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
runTest(AppendClasspath::testDefaultBase);
|
||||
}
|
||||
|
||||
static void testDefaultBase() throws Exception {
|
||||
String topArchiveName = getNewArchiveName("top");
|
||||
doTest(topArchiveName);
|
||||
}
|
||||
|
||||
private static void doTest(String topArchiveName) throws Exception {
|
||||
String appJar = JarBuilder.getOrCreateHelloJar();
|
||||
String appJar2 = JarBuilder.build("AppendClasspath_HelloMore", "HelloMore");
|
||||
|
||||
int idx = appJar.lastIndexOf(File.separator);
|
||||
String jarName = appJar.substring(idx + 1);
|
||||
String jarDir = appJar.substring(0, idx);
|
||||
// relative path starting with "."
|
||||
runWithRelativePath(null, topArchiveName, jarDir,
|
||||
"-Xlog:class+load",
|
||||
"-Xlog:cds+dynamic=debug,cds=debug",
|
||||
"-cp", "." + File.separator + "hello.jar" + File.pathSeparator + appJar2,
|
||||
"HelloMore")
|
||||
.assertNormalExit(output -> {
|
||||
output.shouldContain("Hello source: shared objects file")
|
||||
.shouldContain("Hello World ... More")
|
||||
.shouldHaveExitValue(0);
|
||||
});
|
||||
|
||||
// relative path starting with ".."
|
||||
idx = jarDir.lastIndexOf(File.separator);
|
||||
String jarSubDir = jarDir.substring(idx + 1);
|
||||
runWithRelativePath(null, topArchiveName, jarDir,
|
||||
"-Xlog:class+load",
|
||||
"-Xlog:cds+dynamic=debug,cds=debug",
|
||||
"-cp",
|
||||
".." + File.separator + jarSubDir + File.separator + "hello.jar" + File.pathSeparator + appJar2,
|
||||
"HelloMore")
|
||||
.assertNormalExit(output -> {
|
||||
output.shouldContain("Hello source: shared objects file")
|
||||
.shouldContain("Hello World ... More")
|
||||
.shouldHaveExitValue(0);
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user