Index: vm/vmcore/include/jarfile_support.h =================================================================== --- vm/vmcore/include/jarfile_support.h (revision 699565) +++ vm/vmcore/include/jarfile_support.h (working copy) @@ -40,6 +40,9 @@ #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 static const int JAR_COMPRESSED = 1; @@ -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,10 @@ m_file_handle = 0; if( m_manifest ) delete m_manifest; m_manifest = NULL; + if (m_modulelibmapping != NULL) + STD_FREE(m_modulelibmapping); } - // parses JAR file and stores its structure inside + // parses JAR file, stores its structure inside, and 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 +262,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 699565) +++ vm/vmcore/include/classloader.h (working copy) @@ -379,11 +379,19 @@ 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); + bool onDemandClassLoading(); + 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 class loading + 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 699565) +++ vm/vmcore/src/class_support/classloader.cpp (working copy) @@ -1173,7 +1173,7 @@ inline void BootstrapClassLoader::SetBCPElement(const char *path, apr_pool_t *tmp_pool) { BCPElement* element; - // check existence of a given path in bootstrap classpath + // check existence of a given path in the bootstrap classpath const char* canoname = port_filepath_canonical(path, tmp_pool); const String* new_path = m_env->string_pool.lookup(canoname); for( element = m_BCPElements.m_first; element; element = element->m_next ) { @@ -1318,6 +1318,12 @@ bcpe->m_jar = NULL; } } + if (mapPropertyFile != NULL) + STD_FREE(mapPropertyFile); + if (signaturePropertyFile != NULL) + STD_FREE(signaturePropertyFile); + if (onDemardJars != NULL) + STD_FREE(onDemardJars); } @@ -1554,7 +1560,196 @@ return kernel_bcp; } +bool BootstrapClassLoader::onDemandClassLoading() +{ + // 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; + if( NULL == m_BCPElements.m_last) { + element = m_BCPElements.m_first; + } else { + 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 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 @@ -1584,8 +1779,6 @@ * NOTE: parse_arguments.cpp defers any override, append or prepend here, * storing everything as properties. */ - - char* xbcp = m_env->VmProperties()->get(XBOOTCLASSPATH); char* prepend = m_env->VmProperties()->get(XBOOTCLASSPATH_P); char* append = m_env->VmProperties()->get(XBOOTCLASSPATH_A); @@ -1593,13 +1786,14 @@ 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); char* vmboot = NULL; + 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); strcpy(vmboot + bcp_p_len, xbcp); m_env->VmProperties()->destroy(xbcp); } - + if(vmboot == NULL) { // Not overridden, so lets build // First, helper jars @@ -1607,8 +1801,13 @@ // Second, read in harmonyvm.properties char* kernel_bcp = get_kernel_path(m_env); // Third, what is provided by LUNI as class library classes - char *luni_path = m_env->JavaProperties()->get(O_A_H_BOOT_CLASS_PATH); - + char *luni_path = m_env->JavaProperties()->get(O_A_H_BOOT_CLASS_PATH); + requiredLib = (char*)STD_MALLOC( + bcp_p_len + + (comp_path ? strlen(comp_path) : 0) + + strlen(kernel_bcp) + + bcp_a_len + + 1); vmboot = (char*)STD_MALLOC( bcp_p_len + (comp_path ? strlen(comp_path) : 0) @@ -1617,20 +1816,28 @@ + bcp_a_len + 1); char* vmboot_start = vmboot + bcp_p_len; + char* requiredLib_start = requiredLib + bcp_p_len; *vmboot = '\0'; // make allocated buffer look like a C-string *vmboot_start = '\0'; + *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); - m_env->JavaProperties()->destroy(luni_path); + onDemardJars = (char*)STD_MALLOC(strlen(luni_path) + 1); + strcpy(onDemardJars, luni_path); + + m_env->JavaProperties()->destroy(luni_path); } } @@ -1642,32 +1849,59 @@ 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); } } + // create temp pool for apr functions + apr_pool_t *tmp_pool; + apr_pool_create(&tmp_pool, NULL); + /* * set VM_BOOT_CLASS_PATH and SUN_BOOT_CLASS_PATH for any code * that needs it + * Before doing that, we first 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); + 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 temp pool for apr functions - apr_pool_t *tmp_pool; - apr_pool_create(&tmp_pool, NULL); // create a bootclasspath collection - - SetClasspathFromString(vmboot, tmp_pool); + SetClasspathFromString(requiredLib, tmp_pool); + // Determine whether on-demand jar parsing is enabled + bool mapFileExist = onDemandClassLoading(); + // Set class patch and extract module library mapping info and signature for on-demand parsed jars + if (!mapFileExist) { + SetClasspathAndExtractModuleLibMapping(); + } + STD_FREE(vmboot); + STD_FREE(requiredLib); + 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 +2335,57 @@ // 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); + // 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 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 699565) +++ 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,11 @@ 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 +248,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,11 +274,10 @@ if(!m_use_mmap) { STD_FREE(buf); } - + strcat(m_modulelibmapping, "\n"); return true; } - bool JarFile::ReadEntry(unsigned char* buf, long entry_offset, int entry_length) { if(!m_use_mmap) {