Index: vm/vmcore/include/jarfile_support.h =================================================================== --- vm/vmcore/include/jarfile_support.h (revision 735894) +++ vm/vmcore/include/jarfile_support.h (working copy) @@ -39,6 +39,9 @@ #include "properties.h" #include "manifest.h" #include "lock_manager.h" +#include "String_Pool.h" +// Define the type of module library mapping type +typedef std::map ModuleLibraryMapping; // flags // bit 0 @@ -208,6 +211,7 @@ class JarFile { const char* m_name; + char* m_modulelibmapping; JarEntryCache* m_entries; bool m_ownCache; Manifest* m_manifest; @@ -230,7 +234,7 @@ public: JarFile(JarEntryCache* jec = NULL) - : m_name(NULL), m_entries(jec), m_ownCache(jec == NULL), m_manifest(NULL), m_file_handle(0), + : m_name(NULL), m_modulelibmapping(NULL), m_entries(jec), m_ownCache(jec == NULL), m_manifest(NULL), m_file_handle(0), m_use_mmap(false), m_mmap(NULL) {} ~JarFile() { if(m_ownCache) @@ -241,8 +245,11 @@ m_file_handle = 0; if( m_manifest ) delete m_manifest; m_manifest = NULL; + if (m_modulelibmapping != NULL) + STD_FREE(m_modulelibmapping); + m_modulelibmapping = NULL; } - // parses JAR file and stores its structure inside + // parses JAR file and stores its structure inside as well as extract module/lib mapping info bool Parse( const char* filename, bool do_map ); // returns entry cache (either shared or owned) JarEntryCache* GetCache() { return m_entries; } @@ -256,6 +263,8 @@ Manifest* GetManifest() { return m_manifest; } // returns JAR file name const char* GetName() { return m_name; } + // return module library mapping info + char* GetModuleLibMapping() {return m_modulelibmapping; } // Return jar from jars array static JarFile* GetJar(int idx) { Index: vm/vmcore/include/classloader.h =================================================================== --- vm/vmcore/include/classloader.h (revision 735894) +++ vm/vmcore/include/classloader.h (working copy) @@ -379,11 +379,20 @@ void SetClasspathFromString(char* prop_string, apr_pool_t *tmp_pool); void SetClasspathFromJarFile(JarFile *jar, apr_pool_t *tmp_pool); void SetBCPElement(const char *path, apr_pool_t *tmp_pool); + // Determine whether on-demand jar parsing is ready + bool isOnDemandJarParsingReady(); + void SetClasspathAndExtractModuleLibMapping(); BCPElements m_BCPElements; Global_Env* m_env; // primitive types array, K_LAST_PRIMITIVE - upper bound of primitive types TypeDesc* primitive_types[K_LAST_PRIMITIVE + 1]; + // These two files are for on-demand jar parsing + char *mapPropertyFile; + char *signaturePropertyFile; + // Save the on-demanded jars + char *onDemardJars; + ModuleLibraryMapping pending_jar_set; }; // class BootstrapClassLoader class UserDefinedClassLoader : public ClassLoader Index: vm/vmcore/src/class_support/classloader.cpp =================================================================== --- vm/vmcore/src/class_support/classloader.cpp (revision 735894) +++ vm/vmcore/src/class_support/classloader.cpp (working copy) @@ -1318,6 +1318,15 @@ bcpe->m_jar = NULL; } } + if (mapPropertyFile != NULL) + STD_FREE(mapPropertyFile); + mapPropertyFile = NULL; + if (signaturePropertyFile != NULL) + STD_FREE(signaturePropertyFile); + signaturePropertyFile = NULL; + if (onDemardJars != NULL) + STD_FREE(onDemardJars); + onDemardJars = NULL; } @@ -1554,7 +1563,191 @@ return kernel_bcp; } +bool BootstrapClassLoader::isOnDemandJarParsingReady() +{ + // create temp pool for apr functions + apr_pool_t *tmp_pool; + apr_pool_create(&tmp_pool, NULL); + /* + * Extract the lib boot directory for on-demand class parsing. + * The module library mapping file is saved in the VM directory + */ + char* javaHome = m_env->JavaProperties()->get("java.home"); + char* bootDirectory = + (char*)STD_MALLOC(strlen(javaHome) + + strlen(PORT_FILE_SEPARATOR_STR) + + strlen("bin") + + strlen(PORT_FILE_SEPARATOR_STR) + + strlen("default") + + strlen(PORT_FILE_SEPARATOR_STR) + + 1); + strcpy(bootDirectory, javaHome); + strcat(bootDirectory, PORT_FILE_SEPARATOR_STR); + strcat(bootDirectory, "bin"); + strcat(bootDirectory, PORT_FILE_SEPARATOR_STR); + strcat(bootDirectory, "default" ); + strcat(bootDirectory, PORT_FILE_SEPARATOR_STR); + + mapPropertyFile = + (char*)STD_MALLOC(strlen(bootDirectory) + + strlen("harmonyclass.properties") + + 1); + strcpy(mapPropertyFile, bootDirectory); + strcat(mapPropertyFile, "harmonyclass.properties"); + + signaturePropertyFile = + (char*)STD_MALLOC(strlen(bootDirectory) + + strlen("harmonyclasssig.properties") + + 1); + strcpy(signaturePropertyFile, bootDirectory); + strcat(signaturePropertyFile, "harmonyclasssig.properties"); + + + /** + * Check existence of the signature file and mapping file + * If the signature file exists, we then compare it with boot libraries in luni path + * If the comparion fails, then we disable the on-demand class loading + */ + apr_finfo_t finfo; + bool mapFileExist = true; // By default, assume the mapping file exists + + if (onDemardJars != NULL + && (apr_stat(&finfo, signaturePropertyFile, APR_FINFO_SIZE, tmp_pool) == APR_SUCCESS) + && (apr_stat(&finfo, mapPropertyFile, APR_FINFO_SIZE, tmp_pool) == APR_SUCCESS)) { + + // Both signature and mapping files exist, we check whether there is change for each module + // If there is some change, then on-demand jar parsing is disabled + Properties signatureprops; + load_properties(bootDirectory, "harmonyclasssig", signatureprops); + char* luniPathCopy = (char*)apr_palloc(tmp_pool, strlen(onDemardJars) + 1); + strcpy(luniPathCopy, onDemardJars); + char *path_name = strtok(luniPathCopy, PORT_PATH_SEPARATOR_STR); + while (path_name) + { + // Format the classpath to a standard absolute path. + const char* canoname = port_filepath_canonical(path_name, tmp_pool); + apr_finfo_t finfo; + + /** + * Only compare the existing jars. For jars contained in bootclasspath.properties, + * but don't exist in fact, no need to process them (such as orb.jar) + */ + if(apr_stat(&finfo, canoname, APR_FINFO_SIZE|APR_FINFO_MTIME, tmp_pool) == APR_SUCCESS) { + if (signatureprops.get(canoname) != NULL) { + // Extract signature for this module file + char timeStamp[20]; + char fileSize[20]; + // The number of 19 means the maximum string char for a long long data type + _snprintf(timeStamp, 19, "%lld", finfo.mtime); + _snprintf(fileSize, 19, "%lld", finfo.size); + char* signatureBuf = (char*)apr_palloc(tmp_pool, + strlen(timeStamp) + + strlen(fileSize) + + 2); + strcpy(signatureBuf, timeStamp); + strcat(signatureBuf, PORT_PATH_SEPARATOR_STR); + strcat(signatureBuf, fileSize); + if (strcmp(signatureBuf, signatureprops.get(canoname)) !=0) { + mapFileExist = false; + break; + } + } else { + mapFileExist = false; + break; + } + } + path_name = strtok(NULL, PORT_PATH_SEPARATOR_STR); + } + + if (mapFileExist) { + // Read the map between module and libraries and save them in a software cache + Properties modlibprops; + load_properties(bootDirectory, "harmonyclass", modlibprops); + char** keys = modlibprops.get_keys(); + char** cur_key = keys; + char *value = NULL; + while(*cur_key) { + value = modlibprops.get(*cur_key); + pending_jar_set.insert(std::make_pair(m_env->string_pool.lookup(*cur_key), m_env->string_pool.lookup(value)));; + modlibprops.destroy(value); + cur_key++; + } + if (*keys != NULL) + modlibprops.destroy(keys); + } + }else { + mapFileExist = false; + } + + STD_FREE(bootDirectory); + // destroy temp pool + apr_pool_destroy(tmp_pool); + return mapFileExist; +} + +void BootstrapClassLoader::SetClasspathAndExtractModuleLibMapping() +{ + // create temp pool for apr functions + apr_pool_t *tmp_pool; + apr_pool_create(&tmp_pool, NULL); + + apr_file_t *mappingFile = NULL; + apr_file_t *signatureFile = NULL; + + if (onDemardJars != NULL ) { + apr_status_t status = + apr_file_open(&mappingFile, mapPropertyFile, APR_WRITE | APR_CREATE | APR_TRUNCATE, APR_OS_DEFAULT, tmp_pool); + assert(!status); + status = + apr_file_open(&signatureFile, signaturePropertyFile, APR_WRITE | APR_CREATE | APR_TRUNCATE, APR_OS_DEFAULT, tmp_pool); + assert(!status); + char *path_name = strtok(onDemardJars, PORT_PATH_SEPARATOR_STR); + while (path_name) { + // Format the classpath to a standard absolute path. + const char* canoname = port_filepath_canonical(path_name, tmp_pool); + apr_finfo_t finfo; + if(apr_stat(&finfo, canoname, APR_FINFO_SIZE|APR_FINFO_MTIME, tmp_pool) == APR_SUCCESS) { + // If file exists, we parse it + SetBCPElement(canoname, tmp_pool); + // Extract module lib mapping info + BCPElement* element = m_BCPElements.m_last; + char *mapInfo = NULL; + if (element->m_isJarFile) { + mapInfo = element->m_jar->GetModuleLibMapping(); + apr_size_t len = strlen(mapInfo); + apr_file_write(mappingFile, mapInfo, &len); + } + + // Extract and save signature to the signature file + char timeStamp[20]; + char fileSize[20]; + // The number of 19 means the maximum string char for a long long data type + _snprintf(timeStamp, 19, "%lld", finfo.mtime); + _snprintf(fileSize, 19, "%lld", finfo.size); + char* signatureBuf = (char*)apr_palloc(tmp_pool, strlen(canoname) + + strlen(timeStamp) + + strlen(fileSize) + + strlen(PORT_PATH_SEPARATOR_STR) + + 3); // 3 includes =, \n, and \0 + strcpy(signatureBuf, canoname); + strcat(signatureBuf, "="); + strcat(signatureBuf, timeStamp); + strcat(signatureBuf, PORT_PATH_SEPARATOR_STR); + strcat(signatureBuf, fileSize); + strcat(signatureBuf, "\n"); + apr_size_t len = strlen(signatureBuf); + apr_file_write(signatureFile, signatureBuf, &len); + } + path_name = strtok(NULL, PORT_PATH_SEPARATOR_STR); + } + apr_file_close(mappingFile); + apr_file_close(signatureFile); + } + // destroy temp pool + apr_pool_destroy(tmp_pool); +} + bool BootstrapClassLoader::Initialize(ManagedObject* UNREF loader) { // init common class loader internals @@ -1592,7 +1785,11 @@ size_t bcp_p_len = (prepend ? strlen(prepend) + strlen(PORT_PATH_SEPARATOR_STR) : 0); size_t bcp_a_len = (append ? strlen(PORT_PATH_SEPARATOR_STR) + strlen(append) : 0); + // vmboot is for gather and setting class path char* vmboot = NULL; + // requiredLib contains all lib elements rather than the ones in luni + // when on-demand jar parsing is enabled, only elements in requiredlib are parsed all at once + char* requiredLib = NULL; // If boot classpath is overridden, just use that value. if(xbcp) { vmboot = (char*)STD_MALLOC(strlen(xbcp) + bcp_a_len + bcp_p_len + 1); @@ -1620,16 +1817,30 @@ *vmboot = '\0'; // make allocated buffer look like a C-string *vmboot_start = '\0'; + requiredLib = (char*)STD_MALLOC( + bcp_p_len + + (comp_path ? strlen(comp_path) : 0) + + strlen(kernel_bcp) + + bcp_a_len + + 1); + char* requiredLib_start = requiredLib + bcp_p_len; + *requiredLib = '\0'; + *requiredLib_start = '\0'; + if(comp_path != NULL) { strcpy(vmboot_start, comp_path); + strcpy(requiredLib_start, comp_path); STD_FREE(comp_path); } strcat(vmboot_start, kernel_bcp); + strcat(requiredLib_start, kernel_bcp); STD_FREE(kernel_bcp); if(luni_path != NULL) { strcat(vmboot_start, PORT_PATH_SEPARATOR_STR); strcat(vmboot_start, luni_path); + onDemardJars = (char*)STD_MALLOC(strlen(luni_path) + 1); + strcpy(onDemardJars, luni_path); m_env->JavaProperties()->destroy(luni_path); } } @@ -1642,12 +1853,16 @@ if (prepend) { strcpy(vmboot, prepend); vmboot[bcp_p_len-1] = PORT_PATH_SEPARATOR; + strcpy(requiredLib, prepend); + requiredLib[bcp_p_len-1] = PORT_PATH_SEPARATOR; m_env->VmProperties()->destroy(prepend); } if (append) { strcat(vmboot, PORT_PATH_SEPARATOR_STR); strcat(vmboot, append); + strcat(requiredLib, PORT_PATH_SEPARATOR_STR); + strcat(requiredLib, append); m_env->VmProperties()->destroy(append); } } @@ -1655,19 +1870,43 @@ /* * set VM_BOOT_CLASS_PATH and SUN_BOOT_CLASS_PATH for any code * that needs it + * Before doing that, we exclude any repeated elements */ - m_env->VmProperties()->set(VM_BOOT_CLASS_PATH, vmboot); - m_env->JavaProperties()->set(VM_BOOT_CLASS_PATH, vmboot); - m_env->JavaProperties()->set(SUN_BOOT_CLASS_PATH, vmboot); // create temp pool for apr functions apr_pool_t *tmp_pool; apr_pool_create(&tmp_pool, NULL); + + char *cleanVMBoot = (char*)STD_MALLOC(strlen(vmboot) + 1); + cleanVMBoot[0] = '\0'; + char *path_name = strtok(vmboot, PORT_PATH_SEPARATOR_STR); + while (path_name) + { + //Format the classpath to a standard absolute path. + const char* canoname = port_filepath_canonical(path_name, tmp_pool); + if (strstr(cleanVMBoot, canoname) == NULL) { + strcat(cleanVMBoot, canoname); + strcat(cleanVMBoot, PORT_PATH_SEPARATOR_STR); + } + path_name = strtok(NULL, PORT_PATH_SEPARATOR_STR); + } + + m_env->VmProperties()->set(VM_BOOT_CLASS_PATH, cleanVMBoot); + m_env->JavaProperties()->set(VM_BOOT_CLASS_PATH, cleanVMBoot); + m_env->JavaProperties()->set(SUN_BOOT_CLASS_PATH, cleanVMBoot); // create a bootclasspath collection + SetClasspathFromString(requiredLib, tmp_pool); + // Determine whether on-demand jar parsing is enabled + bool mapFileExist = isOnDemandJarParsingReady(); + // Set class patch and extract module library mapping info and signature for on-demand parsed jars + if (!mapFileExist) { + SetClasspathAndExtractModuleLibMapping(); + } - SetClasspathFromString(vmboot, tmp_pool); STD_FREE(vmboot); + STD_FREE(cleanVMBoot); + STD_FREE(cleanVMBoot); // check if vm.bootclasspath.appendclasspath property is set to true if( TRUE == vm_property_get_boolean("vm.bootclasspath.appendclasspath", FALSE, VM_PROPERTIES) ) { @@ -2101,12 +2340,63 @@ // find archive entry in archive file *not_found = false; const JarEntry *entry = jar_file->Lookup(class_name_in_jar); + if(!entry) { - // file was not found - *not_found = true; - return NULL; - } - + ModuleLibraryMapping::iterator it= pending_jar_set.begin(); + if (it != pending_jar_set.end()) {//indicate there are some un-parsed modules + int packageSeperator = '/'; + const char *pdest = strrchr( class_name_in_jar, packageSeperator ); + char *pkgName = NULL; + if (pdest != NULL) { + int preChaNum = (int)(pdest - class_name_in_jar) + 1; + pkgName = (char*)STD_MALLOC(preChaNum + 1); + strncpy(pkgName, class_name_in_jar, preChaNum); + pkgName[preChaNum] = '\0'; + } + // Find which jar exports this package + if (pkgName != NULL) { + apr_pool_t *tmp_pool; + apr_pool_create(&tmp_pool, NULL); + while (it != pending_jar_set.end()) { + pdest = strstr( (*it).second->bytes, pkgName ); + if (pdest != NULL) { + // Open this found jar, and read all classes contained in this jar + SetBCPElement((*it).first->bytes, tmp_pool); + // Parse the dependent jars as well + BCPElement* element = m_BCPElements.m_last; + if (element->m_isJarFile) { + SetClasspathFromJarFile(element->m_jar, tmp_pool); + } + // Erase the found jar from pending jar list as it has been parsed just now + pending_jar_set.erase(it++); + } else { + it ++; + } + } + STD_FREE(pkgName); + // destroy temp pool + apr_pool_destroy(tmp_pool); + + // Check whether we found the required class. + entry = jar_file->Lookup(class_name_in_jar); + if(!entry) { + *not_found = true; + return NULL; + } + } else { + /** + * We assume the class without any package info is the main class. + * It is the responsibility of user classloader for such class + */ + *not_found = true; + return NULL; + } + }else { + *not_found = true; + return NULL; + } + } + // unpack entry unsigned size = entry->GetContentSize(); unsigned char* buffer = (unsigned char*)STD_MALLOC(size); Index: vm/vmcore/src/util/jarfile_support.cpp =================================================================== --- vm/vmcore/src/util/jarfile_support.cpp (revision 735894) +++ vm/vmcore/src/util/jarfile_support.cpp (working copy) @@ -19,7 +19,7 @@ * @version $Revision: 1.1.2.2.4.4 $ */ #include "jarfile_support.h" - +#include "port_filepath.h" #include "port_malloc.h" #include #include @@ -224,6 +224,10 @@ buf = (unsigned char*)m_mmap->mm + offsetCD; } + // The number of 3 includes character, like =, \n, and \0 + m_modulelibmapping = (char*)STD_MALLOC(strlen(fileName) + 3); + strcpy(m_modulelibmapping, fileName); + strcat(m_modulelibmapping, "="); off = 0; for (int i = 0; i < number; i++){ if (!je.IsPK(buf + off) || !je.IsDirEntryMagic( buf + off + 2)) @@ -243,6 +247,21 @@ return false; } } else { + // Extract all libraries info contained in this module + if (strstr(je.m_fileName, ".class") == NULL && (je.m_fileName[strlen(je.m_fileName)-1] == '/')){ + char *oldMap = m_modulelibmapping; + m_modulelibmapping = (char*)STD_MALLOC( + strlen(oldMap) + + strlen(je.m_fileName) + + 3); + strcpy(m_modulelibmapping, oldMap); + strcat(m_modulelibmapping, je.m_fileName); + strcat(m_modulelibmapping, PORT_PATH_SEPARATOR_STR); + if (oldMap != m_modulelibmapping) { + STD_FREE(oldMap); + } + } + // Insertion in common cache preserves the order of inserted // elements with the same hash value, so lookup will return // the first occurrence. Although more memory is spent here, @@ -254,7 +273,7 @@ if(!m_use_mmap) { STD_FREE(buf); } - + strcat(m_modulelibmapping, "\n"); return true; }