This Bugzilla instance is a read-only archive of historic NetBeans bug reports. To report a bug in NetBeans please follow the project's instructions for reporting issues.

View | Details | Raw Unified | Return to bug 26786
Collapse All | Expand All

(-)core/src/org/netbeans/core/modules/Module.java (-9 / +10 lines)
Lines 505-522 Link Here
505
    /** Open the JAR, load its manifest, and do related things. */
505
    /** Open the JAR, load its manifest, and do related things. */
506
    private void loadManifest() throws IOException {
506
    private void loadManifest() throws IOException {
507
        Util.err.log("loading manifest of " + jar);
507
        Util.err.log("loading manifest of " + jar);
508
        JarFile jarFile;
509
        if (reloadable) {
508
        if (reloadable) {
509
            // Never try to cache reloadable JARs.
510
            ensurePhysicalJar();
510
            ensurePhysicalJar();
511
            jarFile = new JarFile(physicalJar);
511
            JarFile jarFile = new JarFile(physicalJar, false);
512
            try {
513
                Manifest m = jarFile.getManifest();
514
                if (m == null) throw new IOException("No manifest found in " + physicalJar); // NOI18N
515
                manifest = m;
516
            } finally {
517
                jarFile.close();
518
            }
512
        } else {
519
        } else {
513
            jarFile = new JarFile(jar);
520
            manifest = mgr.loadManifest(jar);
514
        }
515
        try {
516
            Manifest m = jarFile.getManifest();
517
            manifest = m;
518
        } finally {
519
            jarFile.close();
520
        }
521
        }
521
    }
522
    }
522
    
523
    
(-)core/src/org/netbeans/core/modules/ModuleInstaller.java (+26 lines)
Lines 17-24 Link Here
17
// OUTSIDE OF openide-util.jar! UI AND FILESYSTEM/DATASYSTEM
17
// OUTSIDE OF openide-util.jar! UI AND FILESYSTEM/DATASYSTEM
18
// INTERACTIONS SHOULD GO ELSEWHERE.
18
// INTERACTIONS SHOULD GO ELSEWHERE.
19
19
20
import java.io.File;
21
import java.io.IOException;
20
import java.util.List;
22
import java.util.List;
21
import java.util.Set;
23
import java.util.Set;
24
import java.util.jar.JarFile;
25
import java.util.jar.Manifest;
22
26
23
/** Responsible for actually installing the contents of module JARs into the IDE.
27
/** Responsible for actually installing the contents of module JARs into the IDE.
24
 * While the manager tracks which modules are enabled and their dependencies,
28
 * While the manager tracks which modules are enabled and their dependencies,
Lines 117-122 Link Here
117
     */
121
     */
118
    public boolean shouldDelegateResource(Module m, Module parent, String pkg) {
122
    public boolean shouldDelegateResource(Module m, Module parent, String pkg) {
119
        return true;
123
        return true;
124
    }
125
    
126
    /** Scan a disabled module JAR file for its manifest contents.
127
     * Subclasses may implement this efficiently, e.g. to use a special cache.
128
     * <p>The default implementation simply opens the JAR and gets its manifest
129
     * using the standard JRE calls.
130
     * <p>Never called for reloadable JARs.
131
     * @param jar a module JAR to open
132
     * @return its manifest
133
     * @throws IOException if the JAR cannot be opened or does not have a manifest at all
134
     * @since org.netbeans.core/1 1.5
135
     * @see #26786
136
     */
137
    public Manifest loadManifest(File jar) throws IOException {
138
        JarFile jarFile = new JarFile(jar, false);
139
        try {
140
            Manifest m = jarFile.getManifest();
141
            if (m == null) throw new IOException("No manifest found in " + jar); // NOI18N
142
            return m;
143
        } finally {
144
            jarFile.close();
145
        }
120
    }
146
    }
121
147
122
}
148
}
(-)core/src/org/netbeans/core/modules/ModuleManager.java (+4 lines)
Lines 473-478 Link Here
473
        // The installer can perform additional checks:
473
        // The installer can perform additional checks:
474
        return installer.shouldDelegateResource(m, parent, pkg);
474
        return installer.shouldDelegateResource(m, parent, pkg);
475
    }
475
    }
476
    // Again, access from Module to ModuleInstaller:
477
    Manifest loadManifest(File jar) throws IOException {
478
        return installer.loadManifest(jar);
479
    }
476
480
477
    private void subCreate(Module m) throws DuplicateException {
481
    private void subCreate(Module m) throws DuplicateException {
478
        Util.err.log("created: " + m);
482
        Util.err.log("created: " + m);
(-)core/src/org/netbeans/core/modules/NbInstaller.java (+238 lines)
Lines 34-39 Link Here
34
import org.openide.util.Utilities;
34
import org.openide.util.Utilities;
35
import org.openide.util.lookup.InstanceContent;
35
import org.openide.util.lookup.InstanceContent;
36
import java.beans.BeanInfo;
36
import java.beans.BeanInfo;
37
import java.util.jar.Manifest;
37
import org.openide.ErrorManager;
38
import org.openide.ErrorManager;
38
import org.openide.execution.NbClassPath;
39
import org.openide.execution.NbClassPath;
39
40
Lines 257-262 Link Here
257
	ev.log(Events.PERF_END, "NbInstaller.load - ModuleInstalls"); // NOI18N
258
	ev.log(Events.PERF_END, "NbInstaller.load - ModuleInstalls"); // NOI18N
258
259
259
        ev.log(Events.FINISH_LOAD, modules);
260
        ev.log(Events.FINISH_LOAD, modules);
261
        
262
        maybeSaveManifestCache();
260
    }
263
    }
261
    
264
    
262
    public void unload(List modules) {
265
    public void unload(List modules) {
Lines 1016-1021 Link Here
1016
        Iterator it = jars.iterator();
1019
        Iterator it = jars.iterator();
1017
        while (it.hasNext()) {
1020
        while (it.hasNext()) {
1018
            cp.add(((File)it.next()).getAbsolutePath() + qualification);
1021
            cp.add(((File)it.next()).getAbsolutePath() + qualification);
1022
        }
1023
    }
1024
    
1025
    // Manifest caching: #26786.
1026
    
1027
    /** The actual file where the manifest cache is stored,
1028
     * or null if it will not be used.
1029
     * Binary format:
1030
     * Sequence of records, one per cached manifest; no particular order.
1031
     * Record:
1032
     * 1. Absolute JAR path, UTF-8.
1033
     * 2. Null byte.
1034
     * 3. Last modification time of JAR, System.currentTimeMillis format, big-endian (8-byte long).
1035
     * 4. The manifest body.
1036
     * 5. Null byte.
1037
     */
1038
    private File manifestCacheFile;
1039
    
1040
    /** While true, try to use the manifest cache.
1041
     * So (non-reloadable) JARs scanned during startup will have their manifests cached.
1042
     * After the primary set of modules has been scanned, this will be set to false.
1043
     * Initially true, unless -J-Dnetbeans.cache.manifests=false is specified,
1044
     * or there is no available cache directory.
1045
     */
1046
    private boolean usingManifestCache;
1047
1048
    {
1049
        usingManifestCache = Boolean.valueOf(System.getProperty("netbeans.cache.manifests", "true")).booleanValue();
1050
        if (usingManifestCache) {
1051
            String userdir = System.getProperty("netbeans.user");
1052
            if (userdir != null) {
1053
                manifestCacheFile = new File(new File(new File(userdir), "cache"), "all-manifests.dat"); // NOI18N
1054
                Util.err.log("Using manifest cache in " + manifestCacheFile);
1055
            } else {
1056
                // Some special startup mode, e.g. with Plain.
1057
                usingManifestCache = false;
1058
                Util.err.log("Not using any manifest cache; no user directory");
1059
            }
1060
        } else {
1061
            Util.err.log("Manifest cache disabled");
1062
        }
1063
    }
1064
    
1065
    /** Cache of known JAR manifests.
1066
     * Initially null. If the cache is read, it may be used to quickly serve JAR manifests.
1067
     * Each JAR file is mapped to a two-element array consisting of
1068
     * its modification date when last read; and the manifest itself.
1069
     */
1070
    private Map manifestCache = null; // Map<File,[Date,Manifest]>
1071
    
1072
    /** If true, at least one manifest has had to be read explicitly.
1073
     * This might be because the cache did not initially exist;
1074
     * or the JAR was not present in the cache;
1075
     * or the JAR was present but did not match the cache timestamp.
1076
     */
1077
    private boolean manifestCacheDirty = false;
1078
    
1079
    // XXX consider logging using Events
1080
    
1081
    /** Overrides superclass method to keep a cache of module manifests,
1082
     * so that their JARs do not have to be opened twice during startup.
1083
     */
1084
    public Manifest loadManifest(File jar) throws IOException {
1085
        if (!usingManifestCache) {
1086
            return super.loadManifest(jar);
1087
        }
1088
        if (manifestCache == null) {
1089
            manifestCache = loadManifestCache(manifestCacheFile);
1090
        }
1091
        Object[] entry = (Object[])manifestCache.get(jar);
1092
        if (entry != null) {
1093
            if (((Date)entry[0]).getTime() == jar.lastModified()) {
1094
                // Cache hit.
1095
                Util.err.log("Found manifest for " + jar + " in cache");
1096
                return (Manifest)entry[1];
1097
            } else {
1098
                Util.err.log("Wrong timestamp for " + jar + " in manifest cache");
1099
            }
1100
        } else {
1101
            Util.err.log("No entry for " + jar + " in manifest cache");
1102
        }
1103
        // Cache miss.
1104
        Manifest m = super.loadManifest(jar);
1105
        // (If that threw IOException, we leave it out of the cache.)
1106
        manifestCache.put(jar, new Object[] {new Date(jar.lastModified()), m});
1107
        manifestCacheDirty = true;
1108
        return m;
1109
    }
1110
    
1111
    /** If the manifest cache had been in use, and is now dirty, write it to disk.
1112
     */
1113
    private void maybeSaveManifestCache() {
1114
        if (usingManifestCache && manifestCacheDirty) {
1115
            try {
1116
                saveManifestCache(manifestCache, manifestCacheFile);
1117
            } catch (IOException ioe) {
1118
                Util.err.notify(ErrorManager.WARNING, ioe);
1119
            }
1120
            usingManifestCache = false;
1121
            manifestCacheDirty = false;
1122
            manifestCache = null;
1123
            manifestCacheFile = null;
1124
        }
1125
    }
1126
    
1127
    /** Really save the cache.
1128
     * @see #manifestCacheFile
1129
     */
1130
    private void saveManifestCache(Map manifestCache, File manifestCacheFile) throws IOException {
1131
        Util.err.log("Saving manifest cache");
1132
        manifestCacheFile.getParentFile().mkdirs();
1133
        OutputStream os = new FileOutputStream(manifestCacheFile);
1134
        try {
1135
            try {
1136
                os = new BufferedOutputStream(os);
1137
                Iterator it = manifestCache.entrySet().iterator();
1138
                while (it.hasNext()) {
1139
                    Map.Entry e = (Map.Entry)it.next();
1140
                    File jar = (File)e.getKey();
1141
                    Object[] v = (Object[])e.getValue();
1142
                    long time = ((Date)v[0]).getTime();
1143
                    Manifest m = (Manifest)v[1];
1144
                    os.write(jar.getAbsolutePath().getBytes("UTF-8")); // NOI18N
1145
                    os.write(0);
1146
                    for (int i = 7; i >= 0; i--) {
1147
                        os.write((int)((time >> (i * 8)) & 0xFF));
1148
                    }
1149
                    m.write(os);
1150
                    os.write(0);
1151
                }
1152
            } finally {
1153
                os.close();
1154
            }
1155
        } catch (IOException ioe) {
1156
            // Do not leave behind a bogus half-written file.
1157
            manifestCacheFile.delete();
1158
            throw ioe;
1159
        }
1160
    }
1161
    
1162
    /** Load the cache if present.
1163
     * If not present, or there are problems with it,
1164
     * just create an empty cache.
1165
     * @see #manifestCacheFile
1166
     */
1167
    private Map loadManifestCache(File manifestCacheFile) {
1168
        if (!manifestCacheFile.canRead()) {
1169
            Util.err.log("No manifest cache found at " + manifestCacheFile);
1170
            return new HashMap(200);
1171
        }
1172
        ev.log(Events.PERF_START, "NbInstaller - loadManifestCache"); // NOI18N
1173
        try {
1174
            InputStream is = new FileInputStream(manifestCacheFile);
1175
            try {
1176
                BufferedInputStream bis = new BufferedInputStream(is);
1177
                Map m = new HashMap(200);
1178
                while (true) {
1179
                    bis.mark(1);
1180
                    if (bis.read() == -1) {
1181
                        break;
1182
                    }
1183
                    bis.reset();
1184
                    readManifestCacheEntry(bis, m);
1185
                }
1186
                tmpbuf = null;
1187
                return m;
1188
            } finally {
1189
                is.close();
1190
                ev.log(Events.PERF_END, "NbInstaller - loadManifestCache"); // NOI18N
1191
            }
1192
        } catch (IOException ioe) {
1193
            Util.err.annotate(ioe, ErrorManager.UNKNOWN, "While reading: " + manifestCacheFile, null, null, null); // NOI18N
1194
            Util.err.notify(ErrorManager.WARNING, ioe);
1195
            return new HashMap(200);
1196
        }
1197
    }
1198
    
1199
    /** buf to be filled, starting at beginning of array */
1200
    private byte[] tmpbuf;
1201
    /** bytes successfully read into buf */
1202
    private int pos;
1203
    private void readUpTillNullByte(InputStream is) throws IOException {
1204
        if (tmpbuf == null) {
1205
            tmpbuf = new byte[4096];
1206
        }
1207
        final int size = tmpbuf.length;
1208
        pos = 0;
1209
        while (true) {
1210
            final int toread = size - pos;
1211
            is.mark(toread);
1212
            int read = is.read(tmpbuf, pos, toread);
1213
            if (read == -1) throw new IOException("EOF"); // NOI18N
1214
            int end = pos + read;
1215
            for (int i = pos; i < end; i++) {
1216
                if (tmpbuf[i] == 0) {
1217
                    // It fit into the 4k buffer (as it usually will, I hope).
1218
                    pos = i;
1219
                    is.reset();
1220
                    while (true) {
1221
                        int toskip = i + 1;
1222
                        long s = is.skip(toskip);
1223
                        if (s == 0 || s > toskip) throw new IOException("Cannot skip"); // NOI18N
1224
                        toskip -= s;
1225
                        if (toskip == 0) break;
1226
                    }
1227
                    return;
1228
                }
1229
            }
1230
            // Did not read the null byte.
1231
            if (read < toread) {
1232
                // Neither filled the buffer nor got to the null byte.
1233
                pos = end;
1234
            } else {
1235
                // Did not fit, try again.
1236
                byte[] tmpbuf2 = new byte[size * 2];
1237
                System.arraycopy(tmpbuf, 0, tmpbuf2, 0, size);
1238
                pos = size;
1239
                tmpbuf = tmpbuf2;
1240
            }
1241
            // continue reading
1242
        }
1243
    }
1244
    
1245
    private void readManifestCacheEntry(InputStream is, Map m) throws IOException {
1246
        readUpTillNullByte(is);
1247
        File jar = new File(new String(tmpbuf, 0, pos, "UTF-8")); // NOI18N
1248
        long time = 0L;
1249
        for (int i = 7; i >= 0; i--) {
1250
            time |= (((long)is.read()) << (i * 8));
1251
        }
1252
        readUpTillNullByte(is);
1253
        Manifest mani = new Manifest(new ByteArrayInputStream(tmpbuf, 0, pos));
1254
        m.put(jar, new Object[] {new Date(time), mani});
1255
        if (Util.err.isLoggable(ErrorManager.INFORMATIONAL)) {
1256
            Util.err.log("Manifest cache entry: jar=" + jar + " date=" + new Date(time) + " codename=" + mani.getMainAttributes().getValue("OpenIDE-Module"));
1019
        }
1257
        }
1020
    }
1258
    }
1021
1259

Return to bug 26786