/** * Loads specified JAR. * * @param jarFileInfo * @throws IOException */ private void loadJar(JarFileInfo jarFileInfo) throws IOException { lstJarFile.add(jarFileInfo); try { Enumeration<JarEntry> en = jarFileInfo.jarFile.entries(); final String EXT_JAR = ".jar"; while (en.hasMoreElements()) { JarEntry je = en.nextElement(); if (je.isDirectory()) { continue; } String s = je.getName().toLowerCase(); // JarEntry name if (s.lastIndexOf(EXT_JAR) == s.length() - EXT_JAR.length()) { JarEntryInfo inf = new JarEntryInfo(jarFileInfo, je); File fileTemp = createTempFile(inf); logInfo( LogArea.JAR, "Loading inner JAR %s from temp file %s", inf.jarEntry, getFilename4Log(fileTemp)); loadJar(new JarFileInfo(new JarFile(fileTemp), inf.getName(), jarFileInfo, fileTemp)); } } } catch (JarClassLoaderException e) { throw new RuntimeException("ERROR on loading inner JAR: " + e.getMessageAll()); } } // loadJar()
/** * Loads class from a JAR and searches for all jar-in-jar. * * @param sClassName class to load. * @return Loaded class. * @throws JarClassLoaderException. */ private Class<?> findJarClass(String sClassName) throws JarClassLoaderException { // http://java.sun.com/developer/onlineTraining/Security/Fundamentals // /magercises/ClassLoader/solution/FileClassLoader.java Class<?> c = hmClass.get(sClassName); if (c != null) { return c; } // Char '/' works for Win32 and Unix. String sName = sClassName.replace('.', '/') + ".class"; JarEntryInfo inf = findJarEntry(sName); String jarSimpleName = null; if (inf != null) { jarSimpleName = inf.jarFileInfo.simpleName; definePackage(sClassName, inf); byte[] a_by = inf.getJarBytes(); try { c = defineClass(sClassName, a_by, 0, a_by.length, pd); } catch (ClassFormatError e) { throw new JarClassLoaderException(null, e); } } if (c == null) { throw new JarClassLoaderException(sClassName); } hmClass.put(sClassName, c); logInfo( LogArea.CLASS, "Loaded %s by %s from JAR %s", sClassName, getClass().getName(), jarSimpleName); return c; } // findJarClass()
/** * Using temp files (one per inner JAR/DLL) solves many issues: 1. There are no ways to load JAR * defined in a JarEntry directly into the JarFile object (see also #6 below). 2. Cannot use * memory-mapped files because they are using nio channels, which are not supported by JarFile * ctor. 3. JarFile object keeps opened JAR files handlers for fast access. 4. Deep resource in a * jar-in-jar does not have well defined URL. Making temp file with JAR solves this problem. 5. * Similar issues with native libraries: <code>ClassLoader.findLibrary()</code> accepts ONLY * string with absolute path to the file with native library. 6. Option * "java.protocol.handler.pkgs" does not allow access to nested JARs(?). * * @param inf JAR entry information. * @return temporary file object presenting JAR entry. * @throws JarClassLoaderException */ private File createTempFile(JarEntryInfo inf) throws JarClassLoaderException { // Temp files directory: // WinXP: C:/Documents and Settings/username/Local Settings/Temp/JarClassLoader // Unix: /var/tmp/JarClassLoader if (dirTemp == null) { File dir = new File(System.getProperty("java.io.tmpdir"), TMP_SUB_DIRECTORY); if (!dir.exists()) { dir.mkdir(); } chmod777(dir); // Unix - allow temp directory RW access to all users. if (!dir.exists() || !dir.isDirectory()) { throw new JarClassLoaderException("Cannot create temp directory " + dir.getAbsolutePath()); } dirTemp = dir; } File fileTmp = null; try { fileTmp = File.createTempFile(inf.getName() + ".", null, dirTemp); fileTmp.deleteOnExit(); chmod777(fileTmp); // Unix - allow temp file deletion by any user byte[] a_by = inf.getJarBytes(); BufferedOutputStream os = new BufferedOutputStream(new FileOutputStream(fileTmp)); os.write(a_by); os.close(); return fileTmp; } catch (IOException e) { throw new JarClassLoaderException( String.format("Cannot create temp file '%s' for %s", fileTmp, inf.jarEntry), e); } } // createTempFile()
/** * @see java.lang.ClassLoader#findResource(java.lang.String) * @return A URL object for reading the resource, or null if the resource could not be found. * Example URL: jar:file:C:\...\some.jar!/resources/InnerText.txt */ @Override protected URL findResource(String sName) { logDebug(LogArea.RESOURCE, "findResource: %s", sName); if (isLaunchedFromJar()) { JarEntryInfo inf = findJarEntry(normalizeResourceName(sName)); if (inf != null) { URL url = inf.getURL(); logInfo(LogArea.RESOURCE, "found resource: %s", url); return url; } logInfo(LogArea.RESOURCE, "not found resource: %s", sName); return null; } return super.findResource(sName); } // findResource()
/** * @see java.lang.ClassLoader#findResources(java.lang.String) * @return An enumeration of {@link java.net.URL <tt>URL</tt>} objects for the resources */ @Override public Enumeration<URL> findResources(String sName) throws IOException { logDebug(LogArea.RESOURCE, "getResources: %s", sName); if (isLaunchedFromJar()) { List<JarEntryInfo> lstJarEntry = findJarEntries(normalizeResourceName(sName)); List<URL> lstURL = new ArrayList<URL>(); for (JarEntryInfo inf : lstJarEntry) { URL url = inf.getURL(); if (url != null) { lstURL.add(url); } } return Collections.enumeration(lstURL); } return super.findResources(sName); } // findResources()