| /* |
| * Copyright 1998-2008 Sun Microsystems, Inc. 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. Sun designates this |
| * particular file as subject to the "Classpath" exception as provided |
| * by Sun in the LICENSE file that accompanied this code. |
| * |
| * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, |
| * CA 95054 USA or visit www.sun.com if you need additional information or |
| * have any questions. |
| */ |
| |
| package com.sun.tools.extcheck; |
| |
| import java.util.*; |
| import java.net.MalformedURLException; |
| import java.util.Vector; |
| import java.io.*; |
| import java.util.StringTokenizer; |
| import java.net.URL; |
| import java.util.jar.JarFile; |
| import java.util.jar.JarEntry; |
| import java.util.jar.Manifest; |
| import java.util.jar.Attributes; |
| import java.util.jar.Attributes.Name; |
| import java.net.URLConnection; |
| import java.security.Permission; |
| import java.util.jar.*; |
| import java.net.JarURLConnection; |
| import sun.net.www.ParseUtil; |
| |
| /** |
| * ExtCheck reports on clashes between a specified (target) |
| * jar file and jar files already installed in the extensions |
| * directory. |
| * |
| * @author Benedict Gomes |
| * @since 1.2 |
| */ |
| |
| public class ExtCheck { |
| |
| private static final boolean DEBUG = false; |
| |
| // The following strings hold the values of the version variables |
| // for the target jar file |
| private String targetSpecTitle; |
| private String targetSpecVersion; |
| private String targetSpecVendor; |
| private String targetImplTitle; |
| private String targetImplVersion; |
| private String targetImplVendor; |
| private String targetsealed; |
| |
| /* Flag to indicate whether extra information should be dumped to stdout */ |
| private boolean verboseFlag; |
| |
| /* |
| * Create a new instance of the jar reporting tool for a particular |
| * targetFile. |
| * @param targetFile is the file to compare against. |
| * @param verbose indicates whether to dump filenames and manifest |
| * information (on conflict) to the standard output. |
| */ |
| static ExtCheck create(File targetFile, boolean verbose) { |
| return new ExtCheck(targetFile, verbose); |
| } |
| |
| private ExtCheck(File targetFile, boolean verbose) { |
| verboseFlag = verbose; |
| investigateTarget(targetFile); |
| } |
| |
| |
| private void investigateTarget(File targetFile) { |
| verboseMessage("Target file:" + targetFile); |
| Manifest targetManifest = null; |
| try { |
| File canon = new File(targetFile.getCanonicalPath()); |
| URL url = ParseUtil.fileToEncodedURL(canon); |
| if (url != null){ |
| JarLoader loader = new JarLoader(url); |
| JarFile jarFile = loader.getJarFile(); |
| targetManifest = jarFile.getManifest(); |
| } |
| } catch (MalformedURLException e){ |
| error("Malformed URL "); |
| } catch (IOException e) { |
| error("IO Exception "); |
| } |
| if (targetManifest == null) |
| error("No manifest available in "+targetFile); |
| Attributes attr = targetManifest.getMainAttributes(); |
| if (attr != null) { |
| targetSpecTitle = attr.getValue(Name.SPECIFICATION_TITLE); |
| targetSpecVersion = attr.getValue(Name.SPECIFICATION_VERSION); |
| targetSpecVendor = attr.getValue(Name.SPECIFICATION_VENDOR); |
| targetImplTitle = attr.getValue(Name.IMPLEMENTATION_TITLE); |
| targetImplVersion = attr.getValue(Name.IMPLEMENTATION_VERSION); |
| targetImplVendor = attr.getValue(Name.IMPLEMENTATION_VENDOR); |
| targetsealed = attr.getValue(Name.SEALED); |
| } else { |
| error("No attributes available in the manifest"); |
| } |
| if (targetSpecTitle == null) |
| error("The target file does not have a specification title"); |
| if (targetSpecVersion == null) |
| error("The target file does not have a specification version"); |
| verboseMessage("Specification title:" + targetSpecTitle); |
| verboseMessage("Specification version:" + targetSpecVersion); |
| if (targetSpecVendor != null) |
| verboseMessage("Specification vendor:" + targetSpecVendor); |
| if (targetImplVersion != null) |
| verboseMessage("Implementation version:" + targetImplVersion); |
| if (targetImplVendor != null) |
| verboseMessage("Implementation vendor:" + targetImplVendor); |
| verboseMessage(""); |
| } |
| |
| /** |
| * Verify that none of the jar files in the install directory |
| * has the same specification-title and the same or a newer |
| * specification-version. |
| * |
| * @return Return true if the target jar file is newer |
| * than any installed jar file with the same specification-title, |
| * otherwise return false |
| */ |
| boolean checkInstalledAgainstTarget(){ |
| String s = System.getProperty("java.ext.dirs"); |
| File [] dirs; |
| if (s != null) { |
| StringTokenizer st = |
| new StringTokenizer(s, File.pathSeparator); |
| int count = st.countTokens(); |
| dirs = new File[count]; |
| for (int i = 0; i < count; i++) { |
| dirs[i] = new File(st.nextToken()); |
| } |
| } else { |
| dirs = new File[0]; |
| } |
| |
| boolean result = true; |
| for (int i = 0; i < dirs.length; i++) { |
| String[] files = dirs[i].list(); |
| if (files != null) { |
| for (int j = 0; j < files.length; j++) { |
| try { |
| File f = new File(dirs[i],files[j]); |
| File canon = new File(f.getCanonicalPath()); |
| URL url = ParseUtil.fileToEncodedURL(canon); |
| if (url != null){ |
| result = result && checkURLRecursively(1,url); |
| } |
| } catch (MalformedURLException e){ |
| error("Malformed URL"); |
| } catch (IOException e) { |
| error("IO Exception"); |
| } |
| } |
| } |
| } |
| if (result) { |
| generalMessage("No conflicting installed jar found."); |
| } else { |
| generalMessage("Conflicting installed jar found. " |
| + " Use -verbose for more information."); |
| } |
| return result; |
| } |
| |
| /** |
| * Recursively verify that a jar file, and any urls mentioned |
| * in its class path, do not conflict with the target jar file. |
| * |
| * @param indent is the current nesting level |
| * @param url is the path to the jar file being checked. |
| * @return true if there is no newer URL, otherwise false |
| */ |
| private boolean checkURLRecursively(int indent, URL url) |
| throws IOException |
| { |
| verboseMessage("Comparing with " + url); |
| JarLoader jarloader = new JarLoader(url); |
| JarFile j = jarloader.getJarFile(); |
| Manifest man = j.getManifest(); |
| if (man != null) { |
| Attributes attr = man.getMainAttributes(); |
| if (attr != null){ |
| String title = attr.getValue(Name.SPECIFICATION_TITLE); |
| String version = attr.getValue(Name.SPECIFICATION_VERSION); |
| String vendor = attr.getValue(Name.SPECIFICATION_VENDOR); |
| String implTitle = attr.getValue(Name.IMPLEMENTATION_TITLE); |
| String implVersion |
| = attr.getValue(Name.IMPLEMENTATION_VERSION); |
| String implVendor = attr.getValue(Name.IMPLEMENTATION_VENDOR); |
| String sealed = attr.getValue(Name.SEALED); |
| if (title != null){ |
| if (title.equals(targetSpecTitle)){ |
| if (version != null){ |
| if (version.equals(targetSpecVersion) || |
| isNotOlderThan(version,targetSpecVersion)){ |
| verboseMessage(""); |
| verboseMessage("CONFLICT DETECTED "); |
| verboseMessage("Conflicting file:"+ url); |
| verboseMessage("Installed Version:" + |
| version); |
| if (implTitle != null) |
| verboseMessage("Implementation Title:"+ |
| implTitle); |
| if (implVersion != null) |
| verboseMessage("Implementation Version:"+ |
| implVersion); |
| if (implVendor != null) |
| verboseMessage("Implementation Vendor:"+ |
| implVendor); |
| return false; |
| } |
| } |
| } |
| } |
| } |
| } |
| boolean result = true; |
| URL[] loaderList = jarloader.getClassPath(); |
| if (loaderList != null) { |
| for(int i=0; i < loaderList.length; i++){ |
| if (url != null){ |
| boolean res = checkURLRecursively(indent+1,loaderList[i]); |
| result = res && result; |
| } |
| } |
| } |
| return result; |
| } |
| |
| /** |
| * See comment in method java.lang.Package.isCompatibleWith. |
| * Return true if already is not older than target. i.e. the |
| * target file may be superseded by a file already installed |
| */ |
| private boolean isNotOlderThan(String already,String target) |
| throws NumberFormatException |
| { |
| if (already == null || already.length() < 1) { |
| throw new NumberFormatException("Empty version string"); |
| } |
| |
| // Until it matches scan and compare numbers |
| StringTokenizer dtok = new StringTokenizer(target, ".", true); |
| StringTokenizer stok = new StringTokenizer(already, ".", true); |
| while (dtok.hasMoreTokens() || stok.hasMoreTokens()) { |
| int dver; |
| int sver; |
| if (dtok.hasMoreTokens()) { |
| dver = Integer.parseInt(dtok.nextToken()); |
| } else |
| dver = 0; |
| |
| if (stok.hasMoreTokens()) { |
| sver = Integer.parseInt(stok.nextToken()); |
| } else |
| sver = 0; |
| |
| if (sver < dver) |
| return false; // Known to be incompatible |
| if (sver > dver) |
| return true; // Known to be compatible |
| |
| // Check for and absorb separators |
| if (dtok.hasMoreTokens()) |
| dtok.nextToken(); |
| if (stok.hasMoreTokens()) |
| stok.nextToken(); |
| // Compare next component |
| } |
| // All components numerically equal |
| return true; |
| } |
| |
| |
| /** |
| * Prints out message if the verboseFlag is set |
| */ |
| void verboseMessage(String message){ |
| if (verboseFlag) { |
| System.err.println(message); |
| } |
| } |
| |
| void generalMessage(String message){ |
| System.err.println(message); |
| } |
| |
| /** |
| * Throws a RuntimeException with a message describing the error. |
| */ |
| static void error(String message) throws RuntimeException { |
| throw new RuntimeException(message); |
| } |
| |
| |
| /** |
| * Inner class used to represent a loader of resources and classes |
| * from a base URL. Somewhat modified version of code in |
| * sun.misc.URLClassPath.JarLoader |
| */ |
| private static class JarLoader { |
| private final URL base; |
| private JarFile jar; |
| private URL csu; |
| |
| /* |
| * Creates a new Loader for the specified URL. |
| */ |
| JarLoader(URL url) { |
| String urlName = url + "!/"; |
| URL tmpBaseURL = null; |
| try { |
| tmpBaseURL = new URL("jar","",urlName); |
| jar = findJarFile(url); |
| csu = url; |
| } catch (MalformedURLException e) { |
| ExtCheck.error("Malformed url "+urlName); |
| } catch (IOException e) { |
| ExtCheck.error("IO Exception occurred"); |
| } |
| base = tmpBaseURL; |
| |
| } |
| |
| /* |
| * Returns the base URL for this Loader. |
| */ |
| URL getBaseURL() { |
| return base; |
| } |
| |
| JarFile getJarFile() { |
| return jar; |
| } |
| |
| private JarFile findJarFile(URL url) throws IOException { |
| // Optimize case where url refers to a local jar file |
| if ("file".equals(url.getProtocol())) { |
| String path = url.getFile().replace('/', File.separatorChar); |
| File file = new File(path); |
| if (!file.exists()) { |
| throw new FileNotFoundException(path); |
| } |
| return new JarFile(path); |
| } |
| URLConnection uc = getBaseURL().openConnection(); |
| //uc.setRequestProperty(USER_AGENT_JAVA_VERSION, JAVA_VERSION); |
| return ((JarURLConnection)uc).getJarFile(); |
| } |
| |
| |
| /* |
| * Returns the JAR file local class path, or null if none. |
| */ |
| URL[] getClassPath() throws IOException { |
| Manifest man = jar.getManifest(); |
| if (man != null) { |
| Attributes attr = man.getMainAttributes(); |
| if (attr != null) { |
| String value = attr.getValue(Name.CLASS_PATH); |
| if (value != null) { |
| return parseClassPath(csu, value); |
| } |
| } |
| } |
| return null; |
| } |
| |
| /* |
| * Parses value of the Class-Path manifest attribute and returns |
| * an array of URLs relative to the specified base URL. |
| */ |
| private URL[] parseClassPath(URL base, String value) |
| throws MalformedURLException |
| { |
| StringTokenizer st = new StringTokenizer(value); |
| URL[] urls = new URL[st.countTokens()]; |
| int i = 0; |
| while (st.hasMoreTokens()) { |
| String path = st.nextToken(); |
| urls[i] = new URL(base, path); |
| i++; |
| } |
| return urls; |
| } |
| } |
| |
| |
| } |