aboutsummaryrefslogtreecommitdiff
path: root/src/share/classes/com/sun/tools/extcheck/ExtCheck.java
blob: b69cf00cd8bd799cbd4633c03faea623d9fb07a7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
/*
 * 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;
        }
    }


}