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 21114
Collapse All | Expand All

(-)core/src/org/netbeans/core/modules/JarClassLoader.java (-4 / +76 lines)
Lines 23-32 Link Here
23
import java.net.MalformedURLException;
23
import java.net.MalformedURLException;
24
import java.security.*;
24
import java.security.*;
25
import java.security.cert.Certificate;
25
import java.security.cert.Certificate;
26
import java.util.Enumeration;
26
import java.util.*;
27
import java.util.Vector;
27
28
import java.util.List;
28
import org.openide.ErrorManager;
29
import java.util.Iterator;
30
29
31
/**
30
/**
32
 * A ProxyClassLoader capable of loading classes from a set of jar files
31
 * A ProxyClassLoader capable of loading classes from a set of jar files
Lines 39-44 Link Here
39
    private URL[] urls;
38
    private URL[] urls;
40
    private ProtectionDomain[] pds; 
39
    private ProtectionDomain[] pds; 
41
    private int count;
40
    private int count;
41
    /** temp copy JARs which ought to be deleted */
42
    private Set deadJars = null; // Set<JarFile>
42
    
43
    
43
    /** Creates new JarClassLoader */
44
    /** Creates new JarClassLoader */
44
    public JarClassLoader (List files, ClassLoader[] parents) {
45
    public JarClassLoader (List files, ClassLoader[] parents) {
Lines 245-249 Link Here
245
        return v.elements();
246
        return v.elements();
246
    }
247
    }
247
248
249
    /** Try to release any JAR locks held by this classloader.
250
     * @see #21114
251
     */
252
    void releaseLocks() {
253
        if (deadJars != null) throw new IllegalStateException();
254
        deadJars = new HashSet(); // Set<JarFile>
255
        try {
256
            for (int i = 0; i < sources.length; i++) {
257
                if (sources[i] instanceof JarFile) {
258
                    JarFile origJar = (JarFile)sources[i];
259
                    File orig = new File(origJar.getName());
260
                    String name = orig.getName();
261
                    String prefix, suffix;
262
                    int idx = name.lastIndexOf('.');
263
                    if (idx == -1) {
264
                        prefix = name;
265
                        suffix = null;
266
                    } else {
267
                        prefix = name.substring(0, idx);
268
                        suffix = name.substring(idx);
269
                    }
270
                    while (prefix.length() < 3) prefix += "x"; // NOI18N
271
                    File temp = File.createTempFile(prefix, suffix);
272
                    temp.deleteOnExit();
273
                    InputStream is = new FileInputStream(orig);
274
                    try {
275
                        OutputStream os = new FileOutputStream(temp);
276
                        try {
277
                            byte[] buf = new byte[4096];
278
                            int j;
279
                            while ((j = is.read(buf)) != -1) {
280
                                os.write(buf, 0, j);
281
                            }
282
                        } finally {
283
                            os.close();
284
                        }
285
                    } finally {
286
                        is.close();
287
                    }
288
                    JarFile tempJar = new JarFile(temp);
289
                    origJar.close();
290
                    deadJars.add(tempJar);
291
                    sources[i] = tempJar;
292
                    Util.err.log("#21114: replacing " + orig + " with " + temp);
293
                }
294
            }
295
        } catch (IOException ioe) {
296
            Util.err.notify(ErrorManager.INFORMATIONAL, ioe);
297
        }
298
    }
299
    
300
    /** Delete any temporary JARs we were holding on to.
301
     * Also close any other JARs in our list.
302
     */
303
    protected void finalize() throws Throwable {
304
        super.finalize();
305
        if (deadJars != null) {
306
            Iterator it = deadJars.iterator();
307
            while (it.hasNext()) {
308
                JarFile jar = (JarFile)it.next();
309
                jar.close();
310
                new File(jar.getName()).delete();
311
                Util.err.log("#21114: closing and deleting temporary " + jar.getName());
312
            }
313
        }
314
        for (int i = 0; i < sources.length; i++) {
315
            if (sources[i] instanceof JarFile) {
316
                ((JarFile)sources[i]).close();
317
            }
318
        }
319
    }
248
320
249
}
321
}
(-)core/src/org/netbeans/core/modules/Module.java (+15 lines)
Lines 29-34 Link Here
29
import java.util.zip.ZipEntry;
29
import java.util.zip.ZipEntry;
30
import org.openide.modules.SpecificationVersion;
30
import org.openide.modules.SpecificationVersion;
31
import org.openide.modules.Dependency;
31
import org.openide.modules.Dependency;
32
import org.openide.util.WeakSet;
32
33
33
/** Object representing one module, possibly installed.
34
/** Object representing one module, possibly installed.
34
 * Responsible for opening of module JAR file; reading
35
 * Responsible for opening of module JAR file; reading
Lines 84-89 Link Here
84
    private SpecificationVersion specVers;
85
    private SpecificationVersion specVers;
85
    /** currently active module classloader */
86
    /** currently active module classloader */
86
    private ClassLoader classloader = null;
87
    private ClassLoader classloader = null;
88
    /** module classloaders that might have been created before */
89
    private final Set oldClassLoaders = new WeakSet(5); // Set<OneModuleClassLoader>
87
    /** localized properties, only non-null if requested from disabled module */
90
    /** localized properties, only non-null if requested from disabled module */
88
    private Properties localizedProps;
91
    private Properties localizedProps;
89
    
92
    
Lines 772-777 Link Here
772
            Util.err.annotate(ioe, iae);
775
            Util.err.annotate(ioe, iae);
773
            throw ioe;
776
            throw ioe;
774
        }
777
        }
778
        oldClassLoaders.add(classloader);
775
    }
779
    }
776
    /** Turn off the classloader and release all resources. */
780
    /** Turn off the classloader and release all resources. */
777
    void classLoaderDown() {
781
    void classLoaderDown() {
Lines 796-801 Link Here
796
            Util.err.log ("All resources associated with module " + jar + " were successfully released.");
800
            Util.err.log ("All resources associated with module " + jar + " were successfully released.");
797
        }
801
        }
798
        destroyPhysicalJar();
802
        destroyPhysicalJar();
803
    }
804
    
805
    /** Notify the module that it is being deleted. */
806
    void destroy() {
807
        // #21114: try to release all JAR locks
808
        Iterator it = oldClassLoaders.iterator();
809
        while (it.hasNext()) {
810
            OneModuleClassLoader l = (OneModuleClassLoader)it.next();
811
            if (!Boolean.getBoolean("no21114"))//XXX
812
            l.releaseLocks();
813
        }
799
    }
814
    }
800
    
815
    
801
    /** Get the JAR manifest.
816
    /** Get the JAR manifest.
(-)core/src/org/netbeans/core/modules/ModuleManager.java (+1 lines)
Lines 384-389 Link Here
384
        firer.change(new ChangeFirer.Change(m, Module.PROP_VALID, Boolean.TRUE, Boolean.FALSE));
384
        firer.change(new ChangeFirer.Change(m, Module.PROP_VALID, Boolean.TRUE, Boolean.FALSE));
385
        // #14561: some other module might now be uninstallable as a result.
385
        // #14561: some other module might now be uninstallable as a result.
386
        clearProblemCache();
386
        clearProblemCache();
387
        m.destroy();
387
    }
388
    }
388
    private void possibleProviderRemoved(Module m) {
389
    private void possibleProviderRemoved(Module m) {
389
        String[] provides = m.getProvides();
390
        String[] provides = m.getProvides();
(-)core/test/unit/src/org/netbeans/core/modules/ModuleManagerTest.java (-2 / +37 lines)
Lines 21-28 Link Here
21
import java.lang.reflect.*;
21
import java.lang.reflect.*;
22
import org.openide.util.*;
22
import org.openide.util.*;
23
import org.openide.modules.*;
23
import org.openide.modules.*;
24
import java.net.URLClassLoader;
24
import java.net.*;
25
import java.net.URL;
26
import java.util.jar.JarFile;
25
import java.util.jar.JarFile;
27
import java.util.jar.Manifest;
26
import java.util.jar.Manifest;
28
27
Lines 51-56 Link Here
51
    public static void main(String[] args) {
50
    public static void main(String[] args) {
52
        // Turn on verbose logging while developing tests:
51
        // Turn on verbose logging while developing tests:
53
        System.setProperty("org.netbeans.core.modules", "0");
52
        System.setProperty("org.netbeans.core.modules", "0");
53
        // In case run standalone, need a work dir.
54
        if (System.getProperty("nbjunit.workdir") == null) {
55
            // Hope java.io.tmpdir is set...
56
            System.setProperty("nbjunit.workdir", System.getProperty("java.io.tmpdir"));
57
        }
54
        TestRunner.run(new NbTestSuite(ModuleManagerTest.class));
58
        TestRunner.run(new NbTestSuite(ModuleManagerTest.class));
55
    }
59
    }
56
    
60
    
Lines 1195-1200 Link Here
1195
            // 8 - too late
1199
            // 8 - too late
1196
            // 9 - must give some rel vers, else ~ -1
1200
            // 9 - must give some rel vers, else ~ -1
1197
            assertEquals(ok, new HashSet(mgr.simulateEnable(all)));
1201
            assertEquals(ok, new HashSet(mgr.simulateEnable(all)));
1202
        } finally {
1203
            mgr.mutexPrivileged().exitWriteAccess();
1204
        }
1205
    }
1206
    
1207
    /** Test #21114: after deleting a module, its JARs are released.
1208
     * Would probably always pass on Unix, but on Windows it matters.
1209
     */
1210
    public void testModuleDeletion() throws Exception {
1211
        FakeModuleInstaller installer = new FakeModuleInstaller();
1212
        FakeEvents ev = new FakeEvents();
1213
        ModuleManager mgr = new ModuleManager(installer, ev);
1214
        mgr.mutexPrivileged().enterWriteAccess();
1215
        try {
1216
            File jar = new File(getWorkDir(), "copy-of-simple-module.jar");
1217
            copy(new File(jars, "simple-module.jar"), jar);
1218
            Module m = mgr.create(jar, null, false, false, false);
1219
            mgr.enable(m);
1220
            Class c = m.getClassLoader().loadClass("org.foo.Something");
1221
            URL u = m.getClassLoader().getResource("org/foo/Something.class");
1222
            URLConnection uc = u.openConnection();
1223
            assertTrue("using JarURLConnection", uc instanceof JarURLConnection);
1224
            uc.connect();
1225
            mgr.disable(m);
1226
            mgr.delete(m);
1227
            assertTrue("could delete JAR file", jar.delete());
1228
            c = null;
1229
            u = null;
1230
            uc = null;
1231
            System.gc();
1232
            System.runFinalization();
1198
        } finally {
1233
        } finally {
1199
            mgr.mutexPrivileged().exitWriteAccess();
1234
            mgr.mutexPrivileged().exitWriteAccess();
1200
        }
1235
        }
(-)xtest/src/org/netbeans/core/modules/Module.java (+1 lines)
Lines 810-815 Link Here
810
    }
810
    }
811
    /** For compat only! */
811
    /** For compat only! */
812
    void cleanup() {}
812
    void cleanup() {}
813
    void destroy() {}
813
    
814
    
814
    /** Get the JAR manifest.
815
    /** Get the JAR manifest.
815
     * Should never be null, even if disabled.
816
     * Should never be null, even if disabled.

Return to bug 21114