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

(-)a/autoupdate.services/src/org/netbeans/modules/autoupdate/services/InstallManager.java (-10 / +24 lines)
Lines 48-54 Link Here
48
import java.io.OutputStream;
48
import java.io.OutputStream;
49
import java.util.ArrayList;
49
import java.util.ArrayList;
50
import java.util.Collections;
50
import java.util.Collections;
51
import java.util.HashSet;
51
import java.util.List;
52
import java.util.List;
53
import java.util.Set;
52
import java.util.jar.JarEntry;
54
import java.util.jar.JarEntry;
53
import java.util.jar.JarFile;
55
import java.util.jar.JarFile;
54
import java.util.logging.Level;
56
import java.util.logging.Level;
Lines 63-74 Link Here
63
import org.openide.filesystems.FileUtil;
65
import org.openide.filesystems.FileUtil;
64
import org.openide.modules.InstalledFileLocator;
66
import org.openide.modules.InstalledFileLocator;
65
import org.openide.util.Lookup;
67
import org.openide.util.Lookup;
68
import org.openide.util.lookup.ServiceProvider;
66
69
67
/**
70
/**
68
 *
71
 *
69
 * @author Jiri Rechtacek
72
 * @author Jiri Rechtacek
70
 */
73
 */
71
@org.openide.util.lookup.ServiceProvider(service=org.openide.modules.InstalledFileLocator.class)
74
@ServiceProvider(service=InstalledFileLocator.class)
72
public class InstallManager extends InstalledFileLocator{
75
public class InstallManager extends InstalledFileLocator{
73
    
76
    
74
    // special directories in NB files layout
77
    // special directories in NB files layout
Lines 324-329 Link Here
324
    }
327
    }
325
328
326
    public File locate(String relativePath, String codeNameBase, boolean localized) {
329
    public File locate(String relativePath, String codeNameBase, boolean localized) {
330
        // Rarely returns anything so don't bother optimizing.
331
        Set<File> files = locateAll(relativePath, codeNameBase, localized);
332
        return files.isEmpty() ? null : files.iterator().next();
333
    }
334
335
    public @Override Set<File> locateAll(String relativePath, String codeNameBase, boolean localized) {
336
        synchronized (InstallManager.class) {
337
            if (clusters.isEmpty()) {
338
                return Collections.<File>emptySet();
339
            }
340
        }
341
        // XXX #28729: use codeNameBase to search only in the appropriate places
327
        if (relativePath.length() == 0) {
342
        if (relativePath.length() == 0) {
328
            throw new IllegalArgumentException("Cannot look up \"\" in InstalledFileLocator.locate"); // NOI18N
343
            throw new IllegalArgumentException("Cannot look up \"\" in InstalledFileLocator.locate"); // NOI18N
329
        }
344
        }
Lines 355-386 Link Here
355
                    ext = name.substring(i);
370
                    ext = name.substring(i);
356
                }
371
                }
357
                String[] suffixes = org.netbeans.Util.getLocalizingSuffixesFast();
372
                String[] suffixes = org.netbeans.Util.getLocalizingSuffixesFast();
373
                Set<File> files = new HashSet<File>();
358
                for (int j = 0; j < suffixes.length; j++) {
374
                for (int j = 0; j < suffixes.length; j++) {
359
                    String locName = baseName + suffixes[j] + ext;
375
                    String locName = baseName + suffixes[j] + ext;
360
                    File f = locateExactPath(prefix, locName);
376
                    files.addAll(locateExactPath(prefix, locName));
361
                    if (f != null) {
362
                        return f;
363
                    }
364
                }
377
                }
365
                return null;
378
                return files;
366
            } else {
379
            } else {
367
                return locateExactPath(prefix, name);
380
                return locateExactPath(prefix, name);
368
            }
381
            }
369
        
382
        
370
    }
383
    }
371
    
384
372
    /** Search all top dirs for a file. */
385
    /** Search all top dirs for a file. */
373
    private static File locateExactPath(String prefix, String name) {
386
    private static Set<File> locateExactPath(String prefix, String name) {
387
        Set<File> files = new HashSet<File>();
374
        synchronized(InstallManager.class) {
388
        synchronized(InstallManager.class) {
375
            File[] dirs = clusters.toArray(new File[clusters.size()]);
389
            File[] dirs = clusters.toArray(new File[clusters.size()]);
376
            for (int i = 0; i < dirs.length; i++) {
390
            for (int i = 0; i < dirs.length; i++) {
377
                File f = makeFile(dirs[i], prefix, name);
391
                File f = makeFile(dirs[i], prefix, name);
378
                if (f.exists()) {                    
392
                if (f.exists()) {                    
379
                    return f;
393
                    files.add(f);
380
                }
394
                }
381
            }            
395
            }            
382
        }        
396
        }        
383
        return null;
397
        return files;
384
    }
398
    }
385
    
399
    
386
    private static File makeFile(File dir, String prefix, String name) {        
400
    private static File makeFile(File dir, String prefix, String name) {        
(-)a/core.startup/src/org/netbeans/core/startup/InstalledFileLocatorImpl.java (-64 / +108 lines)
Lines 44-51 Link Here
44
import java.io.File;
44
import java.io.File;
45
import java.util.ArrayList;
45
import java.util.ArrayList;
46
import java.util.Arrays;
46
import java.util.Arrays;
47
import java.util.Collections;
47
import java.util.HashMap;
48
import java.util.HashMap;
48
import java.util.HashSet;
49
import java.util.HashSet;
50
import java.util.LinkedHashSet;
49
import java.util.List;
51
import java.util.List;
50
import java.util.Map;
52
import java.util.Map;
51
import java.util.Set;
53
import java.util.Set;
Lines 53-58 Link Here
53
import org.netbeans.Util;
55
import org.netbeans.Util;
54
import org.openide.filesystems.FileUtil;
56
import org.openide.filesystems.FileUtil;
55
import org.openide.modules.InstalledFileLocator;
57
import org.openide.modules.InstalledFileLocator;
58
import org.openide.util.lookup.ServiceProvider;
56
59
57
/**
60
/**
58
 * Ability to locate NBM-installed files.
61
 * Ability to locate NBM-installed files.
Lines 60-73 Link Here
60
 * and finally ${netbeans.home}.
63
 * and finally ${netbeans.home}.
61
 * @author Jesse Glick
64
 * @author Jesse Glick
62
 */
65
 */
63
@org.openide.util.lookup.ServiceProvider(service=org.openide.modules.InstalledFileLocator.class)
66
@ServiceProvider(service=InstalledFileLocator.class)
64
public final class InstalledFileLocatorImpl extends InstalledFileLocator {
67
public final class InstalledFileLocatorImpl extends InstalledFileLocator {
65
    
68
    
66
    /** Default constructor for lookup. */
69
    private final File[] dirs;
67
    public InstalledFileLocatorImpl() {}
70
    public InstalledFileLocatorImpl() {
68
    
69
    private static final File[] dirs;
70
    static {
71
        List<File> _dirs = new ArrayList<File>();
71
        List<File> _dirs = new ArrayList<File>();
72
        addDir(_dirs, System.getProperty("netbeans.user"));
72
        addDir(_dirs, System.getProperty("netbeans.user"));
73
        String nbdirs = System.getProperty("netbeans.dirs"); // #27151
73
        String nbdirs = System.getProperty("netbeans.dirs"); // #27151
Lines 123-152 Link Here
123
    }
123
    }
124
    
124
    
125
    /**
125
    /**
126
     * Currently just searches user dir and install dir(s).
126
     * Searches user dir and install dir(s).
127
     * @see "#28729 for a suggested better impl in AU"
128
     */
127
     */
129
    public File locate(String relativePath, String codeNameBase, boolean localized) {
128
    public File locate(String relativePath, String codeNameBase, boolean localized) {
130
        if (relativePath.length() == 0) {
129
        Set<File> files = doLocate(relativePath, localized, true);
131
            throw new IllegalArgumentException("Cannot look up \"\" in InstalledFileLocator.locate"); // NOI18N
130
        return files.isEmpty() ? null : files.iterator().next();
132
        }
131
    }
133
        if (relativePath.charAt(0) == '/') {
132
    
134
            throw new IllegalArgumentException("Paths passed to InstalledFileLocator.locate should not start with '/': " + relativePath); // NOI18N
133
    public @Override Set<File> locateAll(String relativePath, String codeNameBase, boolean localized) {
135
        }
134
        return doLocate(relativePath, localized, false);
136
        int slashIdx = relativePath.lastIndexOf('/');
135
    }
137
        if (slashIdx == relativePath.length() - 1) {
136
138
            throw new IllegalArgumentException("Paths passed to InstalledFileLocator.locate should not end in '/': " + relativePath); // NOI18N
137
    private Set<File> doLocate(String relativePath, boolean localized, boolean single) {
139
        }
138
        String[] prefixAndName = prefixAndName(relativePath);
140
        
139
        String prefix = prefixAndName[0];
141
        String prefix, name;
140
        String name = prefixAndName[1];
142
        if (slashIdx != -1) {
143
            prefix = relativePath.substring(0, slashIdx + 1);
144
            name = relativePath.substring(slashIdx + 1);
145
            assert name.length() > 0;
146
        } else {
147
            prefix = "";
148
            name = relativePath;
149
        }
150
        synchronized (InstalledFileLocatorImpl.class) {
141
        synchronized (InstalledFileLocatorImpl.class) {
151
            if (localized) {
142
            if (localized) {
152
                int i = name.lastIndexOf('.');
143
                int i = name.lastIndexOf('.');
Lines 158-220 Link Here
158
                    baseName = name.substring(0, i);
149
                    baseName = name.substring(0, i);
159
                    ext = name.substring(i);
150
                    ext = name.substring(i);
160
                }
151
                }
161
                String[] suffixes = org.netbeans.Util.getLocalizingSuffixesFast();
152
                Set<File> files = null;
162
                for (int j = 0; j < suffixes.length; j++) {
153
                for (String suffix : org.netbeans.Util.getLocalizingSuffixesFast()) {
163
                    String locName = baseName + suffixes[j] + ext;
154
                    String locName = baseName + suffix + ext;
164
                    File f = locateExactPath(prefix, locName);
155
                    Set<File> f = locateExactPath(prefix, locName, single);
165
                    if (f != null) {
156
                    if (!f.isEmpty()) {
166
                        return f;
157
                        if (single) {
158
                            return f;
159
                        } else if (files == null) {
160
                            files = f;
161
                        } else {
162
                            files = new LinkedHashSet<File>(files);
163
                            files.addAll(f);
164
                        }
167
                    }
165
                    }
168
                }
166
                }
169
                return null;
167
                return files != null ? files : Collections.<File>emptySet();
170
            } else {
168
            } else {
171
                return locateExactPath(prefix, name);
169
                return locateExactPath(prefix, name, single);
172
            }
170
            }
173
        }
171
        }
174
    }
172
    }
175
    
173
176
    /** Search all top dirs for a file. */
174
    /** Search all top dirs for a file. */
177
    private static File locateExactPath(String prefix, String name) {
175
    private Set<File> locateExactPath(String prefix, String name, boolean single) {
178
        assert Thread.holdsLock(InstalledFileLocatorImpl.class);
176
        assert Thread.holdsLock(InstalledFileLocatorImpl.class);
177
        Set<File> files = null;
179
        if (fileCache != null) {
178
        if (fileCache != null) {
180
            Map<File,Set<String>> fileCachePerPrefix = fileCache.get(prefix);
179
            Map<File,Set<String>> fileCachePerPrefix = fileCachePerPrefix(prefix);
181
            if (fileCachePerPrefix == null) {
182
                fileCachePerPrefix = new HashMap<File,Set<String>>(dirs.length * 2);
183
                for (int i = 0; i < dirs.length; i++) {
184
                    File root = dirs[i];
185
                    File d;
186
                    if (prefix.length() > 0) {
187
                        assert prefix.charAt(prefix.length() - 1) == '/';
188
                        d = new File(root, prefix.substring(0, prefix.length() - 1).replace('/', File.separatorChar));
189
                    } else {
190
                        d = root;
191
                    }
192
                    if (d.isDirectory()) {
193
                        String[] kids = d.list();
194
                        if (kids != null) {
195
                            fileCachePerPrefix.put(root, new HashSet<String>(Arrays.asList(kids)));
196
                        } else {
197
                            Util.err.warning("could not read files in " + d);
198
                        }
199
                    }
200
                }
201
                fileCache.put(prefix, fileCachePerPrefix);
202
            }
203
            for (int i = 0; i < dirs.length; i++) {
180
            for (int i = 0; i < dirs.length; i++) {
204
                Set<String> names = fileCachePerPrefix.get(dirs[i]);
181
                Set<String> names = fileCachePerPrefix.get(dirs[i]);
205
                if (names != null && names.contains(name)) {
182
                if (names != null && names.contains(name)) {
206
                    return makeFile(dirs[i], prefix, name);
183
                    File f = makeFile(dirs[i], prefix, name);
184
                    if (single) {
185
                        return Collections.singleton(f);
186
                    } else if (files == null) {
187
                        files = Collections.singleton(f);
188
                    } else {
189
                        files = new LinkedHashSet<File>(files);
190
                        files.add(f);
191
                    }
207
                }
192
                }
208
            }
193
            }
209
        } else {
194
        } else {
210
            for (int i = 0; i < dirs.length; i++) {
195
            for (int i = 0; i < dirs.length; i++) {
211
                File f = makeFile(dirs[i], prefix, name);
196
                File f = makeFile(dirs[i], prefix, name);
212
                if (f.exists()) {
197
                if (f.exists()) {
213
                    return f;
198
                    if (single) {
199
                        return Collections.singleton(f);
200
                    } else if (files == null) {
201
                        files = Collections.singleton(f);
202
                    } else {
203
                        files = new LinkedHashSet<File>(files);
204
                        files.add(f);
205
                    }
214
                }
206
                }
215
            }
207
            }
216
        }
208
        }
217
        return null;
209
        return files != null ? files : Collections.<File>emptySet();
210
    }
211
212
    private static String[] prefixAndName(String relativePath) {
213
        if (relativePath.length() == 0) {
214
            throw new IllegalArgumentException("Cannot look up \"\" in InstalledFileLocator.locate"); // NOI18N
215
        }
216
        if (relativePath.charAt(0) == '/') {
217
            throw new IllegalArgumentException("Paths passed to InstalledFileLocator.locate should not start with '/': " + relativePath); // NOI18N
218
        }
219
        int slashIdx = relativePath.lastIndexOf('/');
220
        if (slashIdx == relativePath.length() - 1) {
221
            throw new IllegalArgumentException("Paths passed to InstalledFileLocator.locate should not end in '/': " + relativePath); // NOI18N
222
        }
223
224
        String prefix, name;
225
        if (slashIdx != -1) {
226
            prefix = relativePath.substring(0, slashIdx + 1);
227
            name = relativePath.substring(slashIdx + 1);
228
            assert name.length() > 0;
229
        } else {
230
            prefix = "";
231
            name = relativePath;
232
        }
233
        return new String[] {prefix, name};
234
    }
235
236
    private Map<File,Set<String>> fileCachePerPrefix(String prefix) {
237
        assert Thread.holdsLock(InstalledFileLocatorImpl.class);
238
        Map<File,Set<String>> fileCachePerPrefix = fileCache.get(prefix);
239
        if (fileCachePerPrefix == null) {
240
            fileCachePerPrefix = new HashMap<File,Set<String>>(dirs.length * 2);
241
            for (int i = 0; i < dirs.length; i++) {
242
                File root = dirs[i];
243
                File d;
244
                if (prefix.length() > 0) {
245
                    assert prefix.charAt(prefix.length() - 1) == '/';
246
                    d = new File(root, prefix.substring(0, prefix.length() - 1).replace('/', File.separatorChar));
247
                } else {
248
                    d = root;
249
                }
250
                if (d.isDirectory()) {
251
                    String[] kids = d.list();
252
                    if (kids != null) {
253
                        fileCachePerPrefix.put(root, new HashSet<String>(Arrays.asList(kids)));
254
                    } else {
255
                        Util.err.warning("could not read files in " + d);
256
                    }
257
                }
258
            }
259
            fileCache.put(prefix, fileCachePerPrefix);
260
        }
261
        return fileCachePerPrefix;
218
    }
262
    }
219
    
263
    
220
    private static File makeFile(File dir, String prefix, String name) {
264
    private static File makeFile(File dir, String prefix, String name) {
(-)a/core.startup/src/org/netbeans/core/startup/ModuleHistory.java (-80 / +1 lines)
Lines 47-72 Link Here
47
// and ModuleInfo-related things). Should be possible to use without
47
// and ModuleInfo-related things). Should be possible to use without
48
// the rest of core.
48
// the rest of core.
49
49
50
import org.openide.modules.SpecificationVersion;
51
52
/** Representation of the history of the module.
50
/** Representation of the history of the module.
53
 * This includes information such as: whether the module
54
 * has been installed before and now just needs to be restored
55
 * (or in fact where it was installed); the previous version
56
 * of the module, in case it needs to be upgraded.
57
 * Used for communication
58
 * between the module lister and the module installer.
59
 * @author Jesse Glick
51
 * @author Jesse Glick
60
 */
52
 */
61
53
// XXX no longer useful, could probably delete, and deprecate Module.history
62
public final class ModuleHistory {
54
public final class ModuleHistory {
63
    
55
    
64
    private final String jar;
56
    private final String jar;
65
    private int oldMajorVers;
66
    private SpecificationVersion oldSpecVers;
67
    private boolean upgraded;
68
    private byte[] installerState;
69
    private boolean installerStateChanged = false;
70
    
57
    
71
    /** Create a module history with essential information.
58
    /** Create a module history with essential information.
72
     * You also need to specify a relative or absolute JAR name.
59
     * You also need to specify a relative or absolute JAR name.
Lines 74-83 Link Here
74
    public ModuleHistory(String jar) {
61
    public ModuleHistory(String jar) {
75
        assert jar != null;
62
        assert jar != null;
76
        this.jar = jar;
63
        this.jar = jar;
77
        upgraded = false;
78
        oldMajorVers = -1;
79
        oldSpecVers = null;
80
        installerState = null;
81
    }
64
    }
82
    
65
    
83
    /**
66
    /**
Lines 88-153 Link Here
88
        return jar;
71
        return jar;
89
    }
72
    }
90
    
73
    
91
    /** True if this module has been installed before. */
92
    boolean isPreviouslyInstalled() {
93
        return upgraded;
94
    }
95
    
96
    /** The old major version of the module,
97
     * before an upgrade.
98
     * -1 if unspecified, or it has never been installed before.
99
     */
100
    int getOldMajorVersion() {
101
        return oldMajorVers;
102
    }
103
    
104
    /** The old specification version of the module,
105
     * before an upgrade.
106
     * null if unspecified, or it has never been installed before.
107
     */
108
    SpecificationVersion getOldSpecificationVersion() {
109
        return oldSpecVers;
110
    }
111
    
112
    /** Signal that a module has been previously installed,
113
     * marking it as a possible candidate for upgrade.
114
     */
115
    void upgrade(int oldMajorVersion, SpecificationVersion oldSpecificationVersion) {
116
        upgraded = true;
117
        oldMajorVers = oldMajorVersion;
118
        oldSpecVers = oldSpecificationVersion;
119
    }
120
    
121
    /** Get the stored state of the ModuleInstall, if any.
122
     * Currently this would be a serialized bytestream.
123
     * null if unknown or there was no stored state.
124
     */
125
    byte[] getInstallerState() {
126
        return installerState;
127
    }
128
    
129
    /** Set the stored state of the ModuleInstall.
130
     * This may be null to indicate that no state
131
     * needs to be stored. Otherwise it would currently
132
     * be a serialized bytestream.
133
     */
134
    void setInstallerState(byte[] state) {
135
        if (installerState != null && state != null) {
136
            installerStateChanged = true;
137
        }
138
        installerState = state;
139
    }
140
    
141
    /** True if the state of the installer has changed dynamically. */
142
    boolean getInstallerStateChanged() {
143
        return installerStateChanged;
144
    }
145
    
146
    /** Reset history after an uninstall. */
147
    void resetHistory() {
148
        upgraded = false;
149
        installerState = null;
150
        installerStateChanged = false;
151
    }
152
    
153
}
74
}
(-)a/core.startup/src/org/netbeans/core/startup/ModuleList.java (-302 / +40 lines)
Lines 45-51 Link Here
45
import java.beans.PropertyChangeListener;
45
import java.beans.PropertyChangeListener;
46
import java.io.BufferedInputStream;
46
import java.io.BufferedInputStream;
47
import java.io.ByteArrayInputStream;
47
import java.io.ByteArrayInputStream;
48
import java.io.ByteArrayOutputStream;
49
import java.io.CharArrayWriter;
48
import java.io.CharArrayWriter;
50
import java.io.DataOutputStream;
49
import java.io.DataOutputStream;
51
import java.io.File;
50
import java.io.File;
Lines 53-66 Link Here
53
import java.io.IOException;
52
import java.io.IOException;
54
import java.io.InputStream;
53
import java.io.InputStream;
55
import java.io.ObjectInputStream;
54
import java.io.ObjectInputStream;
56
import java.io.ObjectOutput;
57
import java.io.ObjectOutputStream;
55
import java.io.ObjectOutputStream;
58
import java.io.OutputStream;
56
import java.io.OutputStream;
59
import java.io.OutputStreamWriter;
57
import java.io.OutputStreamWriter;
60
import java.io.PushbackInputStream;
58
import java.io.PushbackInputStream;
61
import java.io.Writer;
59
import java.io.Writer;
62
import java.util.ArrayList;
60
import java.util.ArrayList;
63
import java.util.Arrays;
64
import java.util.Collections;
61
import java.util.Collections;
65
import java.util.HashMap;
62
import java.util.HashMap;
66
import java.util.HashSet;
63
import java.util.HashSet;
Lines 69-74 Link Here
69
import java.util.Map;
66
import java.util.Map;
70
import java.util.Set;
67
import java.util.Set;
71
import java.util.TreeMap;
68
import java.util.TreeMap;
69
import java.util.jar.JarFile;
72
import java.util.logging.Level;
70
import java.util.logging.Level;
73
import java.util.logging.Logger;
71
import java.util.logging.Logger;
74
import org.netbeans.DuplicateException;
72
import org.netbeans.DuplicateException;
Lines 89-102 Link Here
89
import org.openide.filesystems.FileUtil;
87
import org.openide.filesystems.FileUtil;
90
import org.openide.modules.Dependency;
88
import org.openide.modules.Dependency;
91
import org.openide.modules.InstalledFileLocator;
89
import org.openide.modules.InstalledFileLocator;
92
import org.openide.modules.ModuleInstall;
93
import org.openide.modules.SpecificationVersion;
90
import org.openide.modules.SpecificationVersion;
94
import org.openide.util.Parameters;
91
import org.openide.util.Parameters;
95
import org.openide.util.RequestProcessor;
92
import org.openide.util.RequestProcessor;
96
import org.openide.util.Utilities;
93
import org.openide.util.Utilities;
97
import org.openide.util.WeakSet;
94
import org.openide.util.WeakSet;
98
import org.openide.util.io.NbObjectInputStream;
99
import org.openide.util.io.NbObjectOutputStream;
100
import org.openide.xml.EntityCatalog;
95
import org.openide.xml.EntityCatalog;
101
import org.openide.xml.XMLUtil;
96
import org.openide.xml.XMLUtil;
102
import org.xml.sax.Attributes;
97
import org.xml.sax.Attributes;
Lines 139-146 Link Here
139
    private boolean triggered = false;
134
    private boolean triggered = false;
140
    /** listener for changes in modules, etc.; see comment on class Listener */
135
    /** listener for changes in modules, etc.; see comment on class Listener */
141
    private final Listener listener = new Listener();
136
    private final Listener listener = new Listener();
142
    /** any module install sers from externalizedModules.ser, from class name to data */
143
    private final Map<String,byte[]> compatibilitySers = new HashMap<String,byte[]>(100);
144
    /** atomic actions I have used to change Modules/*.xml */
137
    /** atomic actions I have used to change Modules/*.xml */
145
    private final Set<FileSystem.AtomicAction> myAtomicActions = Collections.<FileSystem.AtomicAction>synchronizedSet(new WeakSet<FileSystem.AtomicAction>(100));
138
    private final Set<FileSystem.AtomicAction> myAtomicActions = Collections.<FileSystem.AtomicAction>synchronizedSet(new WeakSet<FileSystem.AtomicAction>(100));
146
    
139
    
Lines 189-199 Link Here
189
            if (!f.isFile()) throw new FileNotFoundException(f.getAbsolutePath());
182
            if (!f.isFile()) throw new FileNotFoundException(f.getAbsolutePath());
190
            return f;
183
            return f;
191
        } else {
184
        } else {
192
            f = InstalledFileLocator.getDefault().locate(jar, name, false);
185
            Set<File> jars = InstalledFileLocator.getDefault().locateAll(jar, name, false);
193
            if (f != null) {
186
            if (jars.isEmpty()) {
194
                return f;
187
                throw new FileNotFoundException(jar);
188
            } else if (jars.size() == 1) {
189
                return jars.iterator().next();
195
            } else {
190
            } else {
196
                throw new FileNotFoundException(jar);
191
                // Pick the newest one available.
192
                int major = -1;
193
                SpecificationVersion spec = null;
194
                File newest = null;
195
                for (File candidate : jars) {
196
                    int candidateMajor = -1;
197
                    SpecificationVersion candidateSpec = null;
198
                    JarFile jf = new JarFile(candidate);
199
                    try {
200
                        java.util.jar.Attributes attr = jf.getManifest().getMainAttributes();
201
                        String codename = attr.getValue("OpenIDE-Module");
202
                        if (codename != null) {
203
                            int slash = codename.lastIndexOf('/');
204
                            if (slash != -1) {
205
                                candidateMajor = Integer.parseInt(codename.substring(slash + 1));
206
                            }
207
                        }
208
                        String sv = attr.getValue("OpenIDE-Module-Specification-Version");
209
                        if (sv != null) {
210
                            candidateSpec = new SpecificationVersion(sv);
211
                        }
212
                    } finally {
213
                        jf.close();
214
                    }
215
                    if (newest == null || candidateMajor > major || (spec != null && candidateSpec != null && candidateSpec.compareTo(spec) > 0)) {
216
                        newest = candidate;
217
                        major = candidateMajor;
218
                        spec = candidateSpec;
219
                    }
220
                }
221
                return newest;
197
            }
222
            }
198
        }
223
        }
199
    }
224
    }
Lines 301-497 Link Here
301
        ev.log(Events.PERF_END, "ModuleList.installNew"); // NOI18N
326
        ev.log(Events.PERF_END, "ModuleList.installNew"); // NOI18N
302
    }
327
    }
303
    
328
    
304
    /** Record initial condition of a module installer.
305
     * First, if any stored state of the installer was found on disk,
306
     * that is if it is kept in the ModuleHistory, then deserialize
307
     * it (to the found installer). If there are deserialization errors,
308
     * this step is simply skipped. Or, if there was no prerecorded state,
309
     * and the ModuleInstall class overrides writeExternal or a similar
310
     * serialization-related method, thus indicating that it wishes to
311
     * serialize state, then this initial object is serialized to the
312
     * history's state for comparison with the later value. If there are
313
     * problems with this serialization, the history receives a dummy state
314
     * (empty bytestream) to indicate that something should be saved later.
315
     * If there is no prerecorded state and no writeExternal method, it is
316
     * assumed that serialization is irrelevant to this installer and so the
317
     * state is left null and nothing will be done here or in the postpare method.
318
     * If this installer was mentioned in externalizedModules.ser, also try to
319
     * handle the situation (determine if the state was useful or not etc.).
320
     * Access from NbInstaller.
321
     */
322
    void installPrepare(Module m, ModuleInstall inst) {
323
        if (! (m.getHistory() instanceof ModuleHistory)) {
324
            LOG.fine(m + " had strange history " + m.getHistory() + ", ignoring...");
325
            return;
326
        }
327
        ModuleHistory hist = (ModuleHistory)m.getHistory();
328
        // We might have loaded something from externalizedModules.ser before.
329
        byte[] compatSer = compatibilitySers.get(inst.getClass().getName());
330
        if (compatSer != null) {
331
            LOG.fine("Had some old-style state for " + m);
332
            if (isReallyExternalizable(inst.getClass())) {
333
                // OK, maybe it was not useless, let's see...
334
                // Compare virgin state to what we had; if different, load
335
                // the old state and record that we want to track state.
336
                try {
337
                    ByteArrayOutputStream baos = new ByteArrayOutputStream(1000);
338
                    new NbObjectOutputStream(baos).writeObject(inst);
339
                    baos.close();
340
                    if (Utilities.compareObjects(compatSer, baos.toByteArray())) {
341
                        LOG.fine("Old-style state for " + m + " was gratuitous");
342
                        // leave hist.installerState null
343
                    } else {
344
                        LOG.fine("Old-style state for " + m + " was useful, loading it...");
345
                        // Make sure it is recorded as "changed" in history by writing something
346
                        // fake now. In installPostpare, we will load the new installer state
347
                        // and call setInstallerState again, so the result will be written to disk.
348
                        hist.setInstallerState(new byte[0]);
349
                        // And also load it into the actual installer.
350
                        InputStream is = new ByteArrayInputStream(compatSer);
351
                        Object o = new NbObjectInputStream(is).readObject();
352
                        if (o != inst) throw new ClassCastException("Stored " + o + " but expecting " + inst); // NOI18N
353
                    }
354
                } catch (Exception e) {
355
                    LOG.log(Level.WARNING, null, e);
356
                    // Try later to continue.
357
                    hist.setInstallerState(new byte[0]);
358
                } catch (LinkageError le) {
359
                    LOG.log(Level.WARNING, null, le);
360
                    // Try later to continue.
361
                    hist.setInstallerState(new byte[0]);
362
                }
363
            } else {
364
                LOG.fine(m + " did not want to store install state");
365
                // leave hist.installerState null
366
            }
367
        } else if (hist.getInstallerState() != null) {
368
            // We already have some state, load it now.
369
            LOG.fine("Loading install state for " + m);
370
            try {
371
                InputStream is = new ByteArrayInputStream(hist.getInstallerState());
372
                // Note: NBOOS requires the system class loader to be in order.
373
                // Technically we have not yet fired any changes in it. However,
374
                // assuming that we are not in the first block of modules to be
375
                // loaded (that is, core = bootstraps) this will work because the
376
                // available systemClassLoader is just appended to. Better would
377
                // probably be to use a special ObjectInputStream resolving
378
                // specifically to the module's classloader, as this is far more
379
                // direct and possibly more reliable.
380
                Object o = new NbObjectInputStream(is).readObject();
381
                // The joys of SharedClassObject. The deserialization itself actually
382
                // is assumed to overwrite the state of the install singleton. We
383
                // can only confirm that we actually deserialized the same thing we
384
                // were expecting too (it is too late to revert anything of course).
385
                if (o != inst) throw new ClassCastException("Stored " + o + " but expecting " + inst); // NOI18N
386
            } catch (Exception e) {
387
                // IOException, ClassNotFoundException, and maybe unchecked stuff
388
                LOG.log(Level.WARNING, null, e);
389
                // Nothing else to do, hope that the install object was not corrupted
390
                // by the failed deserialization! If it was, it cannot be saved now.
391
            } catch (LinkageError le) {
392
                LOG.log(Level.WARNING, null, le);
393
            }
394
        } else {
395
            // Virgin installer. First we check if it really cares about serialization
396
            // at all, because it not we do not want to waste time forcing it.
397
            if (isReallyExternalizable(inst.getClass())) {
398
                LOG.fine("Checking pre-install state of " + m);
399
                LOG.warning("Warning: use of writeExternal (or writeReplace) in " + inst.getClass().getName() + " is deprecated; use normal settings instead");
400
                try {
401
                    ByteArrayOutputStream baos = new ByteArrayOutputStream(1000);
402
                    new NbObjectOutputStream(baos).writeObject(inst);
403
                    baos.close();
404
                    // Keep track of the installer's state before we installed it.
405
                    // This will be compared to its state afterwards so we can
406
                    // avoid writing anything if nothing changed, thus avoid
407
                    // polluting the disk.
408
                    hist.setInstallerState(baos.toByteArray());
409
                } catch (Exception e) {
410
                    LOG.log(Level.WARNING, null, e);
411
                    // Remember that it is *supposed* to be serializable to something.
412
                    hist.setInstallerState(new byte[0]);
413
                } catch (LinkageError le) {
414
                    LOG.log(Level.WARNING, null, le);
415
                    hist.setInstallerState(new byte[0]);
416
                }
417
            } else {
418
                // It does not want to store anything. Leave the installer state null
419
                // and continue.
420
                LOG.fine(m + " did not want to store install state");
421
            }
422
        }
423
    }
424
    /** Check if a class (extends ModuleInstall) is truly externalizable,
425
     * e.g. overrides writeExternal.
426
     */
427
    private static boolean isReallyExternalizable(Class clazz) {
428
        Class<?> c;
429
        for (c = clazz; c != ModuleInstall.class && c != Object.class; c = c.getSuperclass()) {
430
            try {
431
                c.getDeclaredMethod("writeExternal", ObjectOutput.class); // NOI18N
432
                // [PENDING] check that m is public, nonstatic, returns Void.TYPE and includes at most
433
                // IOException and unchecked exceptions in its clauses, else die
434
                // OK, it does something nontrivial.
435
                return true;
436
            } catch (NoSuchMethodException nsme) {
437
                // Didn't find it at this level, continue.
438
            }
439
            try {
440
                c.getDeclaredMethod("writeReplace"); // NOI18N
441
                // [PENDING] check that m is nonstatic, returns Object, throws ObjectStreamException
442
                // Designates a serializable replacer, this is special.
443
                return true;
444
            } catch (NoSuchMethodException nsme) {
445
                // Again keep on looking.
446
            }
447
        }
448
        // Hit a superclass.
449
        if (c == Object.class) throw new IllegalArgumentException("Class " + clazz + " was not a ModuleInstall"); // NOI18N
450
        // Hit ModuleInstall. Did not find anything. Assumed to not do anything during externalization.
451
        return false;
452
    }
453
    
454
    /** Acknowledge later conditions of a module installer.
455
     * If the module history indicates a nonnull installer state, then we
456
     * try to serialize the current state to a temporary buffer and compare
457
     * to the previous state. If the serialization succeeds, and they differ,
458
     * then the new state is recorded in the history and will later be written to disk.
459
     * Access from NbInstaller.
460
     */
461
    void installPostpare(Module m, ModuleInstall inst) {
462
        if (! (m.getHistory() instanceof ModuleHistory)) {
463
            LOG.fine(m + " had strange history " + m.getHistory() + ", ignoring...");
464
            return;
465
        }
466
        ModuleHistory hist = (ModuleHistory)m.getHistory();
467
        if (hist.getInstallerState() != null) {
468
            try {
469
                ByteArrayOutputStream baos = new ByteArrayOutputStream(1000);
470
                new NbObjectOutputStream(baos).writeObject(inst);
471
                baos.close();
472
                byte[] old = hist.getInstallerState();
473
                byte[] nue = baos.toByteArray();
474
                if (Utilities.compareObjects(old, nue)) {
475
                    // State has not changed.
476
                    LOG.fine(m + " did not change installer state (" + old.length + " bytes), not writing anything");
477
                } else {
478
                    // It did change. Store new version.
479
                    LOG.fine(m + " changed installer state after loading");
480
                    hist.setInstallerState(nue);
481
                }
482
            } catch (Exception e) {
483
                LOG.log(Level.WARNING, null, e);
484
                // We could not compare, so don't bother writing out any old state.
485
                //hist.setInstallerState(null);
486
            } catch (LinkageError le) {
487
                LOG.log(Level.WARNING, null, le);
488
            }
489
        } else {
490
            // Nothing stored (does not writeExternal), do nothing.
491
            LOG.fine(m + " has no saved state");
492
        }
493
    }
494
    
495
    /** Read an XML file using an XMLReader and parse into a map of properties.
329
    /** Read an XML file using an XMLReader and parse into a map of properties.
496
     * One distinguished property 'name' is the code name base
330
     * One distinguished property 'name' is the code name base
497
     * and is taken from the root element. Others are taken
331
     * and is taken from the root element. Others are taken
Lines 579-594 Link Here
579
     * @return some parsed value suitable for the status map
413
     * @return some parsed value suitable for the status map
580
     */
414
     */
581
    private Object processStatusParam(String k, String v) throws NumberFormatException {
415
    private Object processStatusParam(String k, String v) throws NumberFormatException {
582
        if (k == "release") { // NOI18N
416
        if (k == "enabled" // NOI18N
583
            return Integer.parseInt(v);
584
        } else if (k == "enabled" // NOI18N
585
                   || k == "autoload" // NOI18N
417
                   || k == "autoload" // NOI18N
586
                   || k == "eager" // NOI18N
418
                   || k == "eager" // NOI18N
587
                   || k == "reloadable" // NOI18N
419
                   || k == "reloadable" // NOI18N
588
                   ) {
420
                   ) {
589
            return Boolean.valueOf(v);
421
            return Boolean.valueOf(v);
590
        } else if (k == "specversion") { // NOI18N
591
            return new SpecificationVersion(v);
592
        } else {
422
        } else {
593
            // Other properties are of type String.
423
            // Other properties are of type String.
594
            // Intern the smaller ones which are likely to be repeated somewhere.
424
            // Intern the smaller ones which are likely to be repeated somewhere.
Lines 610-634 Link Here
610
            && ((Boolean)m.get("eager")).booleanValue() // NOI18N
440
            && ((Boolean)m.get("eager")).booleanValue() // NOI18N
611
            && m.get("enabled") != null) // NOI18N
441
            && m.get("enabled") != null) // NOI18N
612
            throw new IOException("Eager modules cannot specify enablement"); // NOI18N
442
            throw new IOException("Eager modules cannot specify enablement"); // NOI18N
613
        // Compatibility:
614
        String origin = (String)m.remove("origin"); // NOI18N
615
        if (origin != null) {
616
            String jar = (String)m.get("jar"); // NOI18N
617
            String newjar;
618
            if (origin.equals("user") || origin.equals("installation")) { // NOI18N
619
                newjar = "modules/" + jar; // NOI18N
620
            } else if (origin.equals("user/autoload") || origin.equals("installation/autoload")) { // NOI18N
621
                newjar = "modules/autoload/" + jar; // NOI18N
622
            } else if (origin.equals("user/eager") || origin.equals("installation/eager")) { // NOI18N
623
                newjar = "modules/eager/" + jar; // NOI18N
624
            } else if (origin.equals("adhoc")) { // NOI18N
625
                newjar = jar;
626
            } else {
627
                throw new IOException("Unrecognized origin " + origin + " for " + jar); // NOI18N
628
            }
629
            LOG.warning("Upgrading 'jar' param from " + jar + " to " + newjar + " and removing 'origin' " + origin);
630
            m.put("jar", newjar); // NOI18N
631
        }
632
    }
443
    }
633
444
634
    // Encoding irrelevant for these getBytes() calls: all are ASCII...
445
    // Encoding irrelevant for these getBytes() calls: all are ASCII...
Lines 874-880 Link Here
874
        for (Map.Entry<String, Object> entry: new TreeMap<String, Object>(m).entrySet()) {
685
        for (Map.Entry<String, Object> entry: new TreeMap<String, Object>(m).entrySet()) {
875
            String name = entry.getKey();
686
            String name = entry.getKey();
876
            if (
687
            if (
877
                name.equals("installerState") || name.equals("name") || // NOI18N
688
                name.equals("name") || // NOI18N
878
                name.equals("deps") // NOI18N
689
                name.equals("deps") // NOI18N
879
            ) {
690
            ) {
880
                // Skip this one, it is a pseudo-param.
691
                // Skip this one, it is a pseudo-param.
Lines 936-971 Link Here
936
                    lock.releaseLock();
747
                    lock.releaseLock();
937
                }
748
                }
938
                //nue.lastApprovedChange = nue.file.lastModified().getTime();
749
                //nue.lastApprovedChange = nue.file.lastModified().getTime();
939
                // Now check up on the installer ser.
940
                byte[] data = (byte[])nue.diskProps.get("installerState"); // NOI18N
941
                if (data != null) {
942
                    String installerName = (String)nue.diskProps.get("installer"); // NOI18N
943
                    FileObject ser = folder.getFileObject(installerName);
944
                    if (ser == null) {
945
                        // Need to make it.
946
                        int idx = installerName.lastIndexOf('.'); // NOI18N
947
                        ser = folder.createData(installerName.substring(0, idx), installerName.substring(idx + 1));
948
                    }
949
                    // Now write it.
950
                    lock = ser.lock();
951
                    try {
952
                        OutputStream os = ser.getOutputStream(lock);
953
                        try {
954
                            os.write(data);
955
                        } finally {
956
                            os.close();
957
                        }
958
                    } finally {
959
                        lock.releaseLock();
960
                    }
961
                } else {
962
                    /* Probably not right:
963
                    // Delete any existing one.
964
                    if (ser != null) {
965
                        ser.delete();
966
                    }
967
                     */
968
                }
969
            }
750
            }
970
        };
751
        };
971
        myAtomicActions.add(aa);
752
        myAtomicActions.add(aa);
Lines 1153-1175 Link Here
1153
    
934
    
1154
    /** Compute what properties we would want to store in XML
935
    /** Compute what properties we would want to store in XML
1155
     * for this module. I.e. 'name', 'reloadable', etc.
936
     * for this module. I.e. 'name', 'reloadable', etc.
1156
     * The special property 'installerState' may be set (only
1157
     * if the normal property 'installer' is also set) and
1158
     * will be a byte[] rather than a string, which means that
1159
     * the indicated installer state should be written out.
1160
     */
937
     */
1161
    private Map<String,Object> computeProperties(Module m) {
938
    private Map<String,Object> computeProperties(Module m) {
1162
        if (m.isFixed() || ! m.isValid()) throw new IllegalArgumentException("fixed or invalid: " + m); // NOI18N
939
        if (m.isFixed() || ! m.isValid()) throw new IllegalArgumentException("fixed or invalid: " + m); // NOI18N
1163
        Map<String,Object> p = new HashMap<String,Object>();
940
        Map<String,Object> p = new HashMap<String,Object>();
1164
        p.put("name", m.getCodeNameBase()); // NOI18N
941
        p.put("name", m.getCodeNameBase()); // NOI18N
1165
        int rel = m.getCodeNameRelease();
1166
        if (rel >= 0) {
1167
            p.put("release", rel); // NOI18N
1168
        }
1169
        SpecificationVersion spec = m.getSpecificationVersion();
1170
        if (spec != null) {
1171
            p.put("specversion", spec); // NOI18N
1172
        }
1173
        if (!m.isAutoload() && !m.isEager()) {
942
        if (!m.isAutoload() && !m.isEager()) {
1174
            p.put("enabled", m.isEnabled()); // NOI18N
943
            p.put("enabled", m.isEnabled()); // NOI18N
1175
        }
944
        }
Lines 1179-1188 Link Here
1179
        if (m.getHistory() instanceof ModuleHistory) {
948
        if (m.getHistory() instanceof ModuleHistory) {
1180
            ModuleHistory hist = (ModuleHistory) m.getHistory();
949
            ModuleHistory hist = (ModuleHistory) m.getHistory();
1181
            p.put("jar", hist.getJar()); // NOI18N
950
            p.put("jar", hist.getJar()); // NOI18N
1182
            if (hist.getInstallerStateChanged()) {
1183
                p.put("installer", m.getCodeNameBase().replace('.', '-') + ".ser"); // NOI18N
1184
                p.put("installerState", hist.getInstallerState()); // NOI18N
1185
            }
1186
        }
951
        }
1187
        return p;
952
        return p;
1188
    }
953
    }
Lines 1636-1642 Link Here
1636
        }
1401
        }
1637
        private void stepCheckMisc(Map<String,Map<String,Object>> dirtyprops) {
1402
        private void stepCheckMisc(Map<String,Map<String,Object>> dirtyprops) {
1638
            LOG.fine("ModuleList: stepCheckMisc");
1403
            LOG.fine("ModuleList: stepCheckMisc");
1639
            String[] toCheck = {"jar", "autoload", "eager", "release", "specversion"}; // NOI18N
1404
            String[] toCheck = {"jar", "autoload", "eager"}; // NOI18N
1640
            for (Map.Entry<String,Map<String,Object>> entry : dirtyprops.entrySet()) {
1405
            for (Map.Entry<String,Map<String,Object>> entry : dirtyprops.entrySet()) {
1641
                String cnb = entry.getKey();
1406
                String cnb = entry.getKey();
1642
                Map<String,Object> props = entry.getValue();
1407
                Map<String,Object> props = entry.getValue();
Lines 1804-1813 Link Here
1804
                        continue;
1569
                        continue;
1805
                    }
1570
                    }
1806
                    ModuleHistory history = new ModuleHistory(jar); // NOI18N
1571
                    ModuleHistory history = new ModuleHistory(jar); // NOI18N
1807
                    Integer prevReleaseI = (Integer) props.get("release"); // NOI18N
1808
                    int prevRelease = prevReleaseI == null ? -1 : prevReleaseI.intValue();
1809
                    SpecificationVersion prevSpec = (SpecificationVersion) props.get("specversion"); // NOI18N
1810
                    history.upgrade(prevRelease, prevSpec);
1811
                    Boolean reloadableB = (Boolean) props.get("reloadable"); // NOI18N
1572
                    Boolean reloadableB = (Boolean) props.get("reloadable"); // NOI18N
1812
                    boolean reloadable = reloadableB != null ? reloadableB.booleanValue() : false;
1573
                    boolean reloadable = reloadableB != null ? reloadableB.booleanValue() : false;
1813
                    boolean enabled = enabledB != null ? enabledB.booleanValue() : false;
1574
                    boolean enabled = enabledB != null ? enabledB.booleanValue() : false;
Lines 1815-1843 Link Here
1815
                    boolean autoload = autoloadB != null ? autoloadB.booleanValue() : false;
1576
                    boolean autoload = autoloadB != null ? autoloadB.booleanValue() : false;
1816
                    Boolean eagerB = (Boolean) props.get("eager"); // NOI18N
1577
                    Boolean eagerB = (Boolean) props.get("eager"); // NOI18N
1817
                    boolean eager = eagerB != null ? eagerB.booleanValue() : false;
1578
                    boolean eager = eagerB != null ? eagerB.booleanValue() : false;
1818
                    String installer = (String) props.get("installer"); // NOI18N
1819
                    if (installer != null) {
1820
                        String nameDashes = name.replace('.', '-');
1821
                        if (!installer.equals(nameDashes + ".ser")) {
1822
                            throw new IOException("Incorrect installer ser name: " + installer); // NOI18N
1823
                        }
1824
                        // Load from disk in mentioned file.
1825
                        FileObject installerSer = folder.getFileObject(nameDashes, "ser"); // NOI18N
1826
                        if (installerSer == null) {
1827
                            throw new IOException("No such install ser: " + installer + "; I see only: " + Arrays.asList(folder.getChildren())); // NOI18N
1828
                        }
1829
                        // Hope the stored state is not >Integer.MAX_INT! :-)
1830
                        byte[] buf = new byte[(int) installerSer.getSize()];
1831
                        InputStream is2 = installerSer.getInputStream();
1832
                        try {
1833
                            is2.read(buf);
1834
                        } finally {
1835
                            is2.close();
1836
                        }
1837
                        history.setInstallerState(buf);
1838
                        // Quasi-prop which is stored separately.
1839
                        props.put("installerState", buf); // NOI18N
1840
                    }
1841
                    NbInstaller.register(name, props.get("deps")); // NOI18N
1579
                    NbInstaller.register(name, props.get("deps")); // NOI18N
1842
                    Module m = mgr.create(jarFile, history, reloadable, autoload, eager);
1580
                    Module m = mgr.create(jarFile, history, reloadable, autoload, eager);
1843
                    NbInstaller.register(null, null);
1581
                    NbInstaller.register(null, null);
(-)a/core.startup/src/org/netbeans/core/startup/NbInstaller.java (-38 / +2 lines)
Lines 417-463 Link Here
417
        if (instClazz != null) {
417
        if (instClazz != null) {
418
            ModuleInstall inst = SharedClassObject.findObject(instClazz, true);
418
            ModuleInstall inst = SharedClassObject.findObject(instClazz, true);
419
            if (load) {
419
            if (load) {
420
                if (moduleList != null) {
420
                ev.log(Events.RESTORE, m);
421
                    moduleList.installPrepare(m, inst);
421
                inst.restored();
422
                }
423
                // restore, install, or upgrade as appropriate
424
                Object history = m.getHistory();
425
                if (history instanceof ModuleHistory) {
426
                    ModuleHistory h = (ModuleHistory)history;
427
                    if (h.isPreviouslyInstalled()) {
428
                        // Check whether we have changed versions.
429
                        SpecificationVersion oldSpec = h.getOldSpecificationVersion();
430
                        SpecificationVersion nueSpec = m.getSpecificationVersion();
431
                        if (m.getCodeNameRelease() != h.getOldMajorVersion() ||
432
                                (oldSpec == null ^ nueSpec == null) ||
433
                                (oldSpec != null && nueSpec != null && oldSpec.compareTo(nueSpec) != 0)) {
434
                            // Yes, different version; upgrade from the old one.
435
                            ev.log(Events.UPDATE, m);
436
                            inst.updated(h.getOldMajorVersion(), oldSpec == null ? null : oldSpec.toString());
437
                        } else {
438
                            // Same version as before.
439
                            ev.log(Events.RESTORE, m);
440
                            inst.restored();
441
                        }
442
                    } else {
443
                        // First time.
444
                        ev.log(Events.INSTALL, m);
445
                        inst.installed();
446
                    }
447
                } else {
448
                    // Probably fixed module. Just restore.
449
                    ev.log(Events.RESTORE, m);
450
                    inst.restored();
451
                }
452
                if (moduleList != null) {
453
                    moduleList.installPostpare(m, inst);
454
                }
455
            } else {
422
            } else {
456
                ev.log(Events.UNINSTALL, m);
423
                ev.log(Events.UNINSTALL, m);
457
                inst.uninstalled();
424
                inst.uninstalled();
458
                if (m.getHistory() instanceof ModuleHistory) {
459
                    ((ModuleHistory)m.getHistory()).resetHistory();
460
                }
461
            }
425
            }
462
        }
426
        }
463
    }
427
    }
(-)a/core.startup/test/unit/src/org/netbeans/core/startup/InstalledFileLocatorImplTest.java (+17 lines)
Lines 45-50 Link Here
45
import java.io.FileOutputStream;
45
import java.io.FileOutputStream;
46
import java.io.IOException;
46
import java.io.IOException;
47
import java.io.OutputStream;
47
import java.io.OutputStream;
48
import java.util.Arrays;
49
import java.util.Collections;
50
import java.util.HashSet;
48
import java.util.Locale;
51
import java.util.Locale;
49
import org.netbeans.junit.NbTestCase;
52
import org.netbeans.junit.NbTestCase;
50
import org.openide.modules.InstalledFileLocator;
53
import org.openide.modules.InstalledFileLocator;
Lines 149-153 Link Here
149
        assertEquals("[no cache] but look in all dirs for most specific resource first", file(nbhome, "h_ja"), ifl.locate("h", null, true));
152
        assertEquals("[no cache] but look in all dirs for most specific resource first", file(nbhome, "h_ja"), ifl.locate("h", null, true));
150
        assertEquals("[no cache] localized lookup a no-op for nonlocalized files", file(nbuser, "a/b"), ifl.locate("a/b", null, true));
153
        assertEquals("[no cache] localized lookup a no-op for nonlocalized files", file(nbuser, "a/b"), ifl.locate("a/b", null, true));
151
    }
154
    }
155
156
    public void testLocateAll() throws Exception {
157
        InstalledFileLocatorImpl.prepareCache();
158
        doTestLocateAll();
159
        InstalledFileLocatorImpl.discardCache();
160
        doTestLocateAll();
161
    }
162
    private void doTestLocateAll() {
163
        assertEquals(new HashSet<File>(Arrays.asList(file(nbuser, "a/b"), file(nbhome, "a/b"))), ifl.locateAll("a/b", null, false));
164
        assertEquals(Collections.emptySet(), ifl.locateAll("nonexistent", null, false));
165
        assertEquals(Collections.emptySet(), ifl.locateAll("nonexistent", null, true));
166
        assertEquals(new HashSet<File>(Arrays.asList(file(nbhome, "loc/y.html"), file(nbdir2, "loc/y_foo.html"))), ifl.locateAll("loc/y.html", null, true));
167
        assertEquals(new HashSet<File>(Arrays.asList(file(nbdir1, "e/f"), file(nbhome, "e/f"))), ifl.locateAll("e/f", null, false));
168
    }
152
    
169
    
153
}
170
}
(-)a/nbbuild/antsrc/org/netbeans/nbbuild/CreateModuleXML.java (-15 lines)
Lines 47-53 Link Here
47
import java.util.jar.*;
47
import java.util.jar.*;
48
import java.io.*;
48
import java.io.*;
49
import java.util.zip.ZipEntry;
49
import java.util.zip.ZipEntry;
50
import org.apache.tools.ant.types.Mapper;
51
50
52
/** Create XML files corresponding to the set of known modules
51
/** Create XML files corresponding to the set of known modules
53
 * without actually running the IDE.
52
 * without actually running the IDE.
Lines 206-222 Link Here
206
                }
205
                }
207
                int idx = codename.lastIndexOf('/');
206
                int idx = codename.lastIndexOf('/');
208
                String codenamebase;
207
                String codenamebase;
209
                int rel;
210
                if (idx == -1) {
208
                if (idx == -1) {
211
                    codenamebase = codename;
209
                    codenamebase = codename;
212
                    rel = -1;
213
                } else {
210
                } else {
214
                    codenamebase = codename.substring(0, idx);
211
                    codenamebase = codename.substring(0, idx);
215
                    try {
216
                        rel = Integer.parseInt(codename.substring(idx + 1));
217
                    } catch (NumberFormatException e) {
218
                        throw new BuildException("Invalid OpenIDE-Module '" + codename + "' in " + module, getLocation());
219
                    }
220
                }
212
                }
221
                File xml = new File(xmldir, codenamebase.replace('.', '-') + ".xml");
213
                File xml = new File(xmldir, codenamebase.replace('.', '-') + ".xml");
222
                if (xml.exists()) {
214
                if (xml.exists()) {
Lines 262-268 Link Here
262
                    displayname = codename;
254
                    displayname = codename;
263
                }
255
                }
264
                names.add(displayname);
256
                names.add(displayname);
265
                String spec = attr.getValue("OpenIDE-Module-Specification-Version");
266
                if (isHidden) {
257
                if (isHidden) {
267
                    File h = new File(xml.getParentFile(), xml.getName() + "_hidden");
258
                    File h = new File(xml.getParentFile(), xml.getName() + "_hidden");
268
                    h.createNewFile();
259
                    h.createNewFile();
Lines 282-294 Link Here
282
                            pw.println("    <param name=\"enabled\">" + isEnabled + "</param>");
273
                            pw.println("    <param name=\"enabled\">" + isEnabled + "</param>");
283
                        }
274
                        }
284
                        pw.println("    <param name=\"jar\">" + kid.replace(File.separatorChar, '/') + "</param>");
275
                        pw.println("    <param name=\"jar\">" + kid.replace(File.separatorChar, '/') + "</param>");
285
                        if (rel != -1) {
286
                            pw.println("    <param name=\"release\">" + rel + "</param>");
287
                        }
288
                        pw.println("    <param name=\"reloadable\">false</param>");
276
                        pw.println("    <param name=\"reloadable\">false</param>");
289
                        if (spec != null) {
290
                            pw.println("    <param name=\"specversion\">" + spec + "</param>");
291
                        }
292
                        pw.println("</module>");
277
                        pw.println("</module>");
293
                        pw.flush();
278
                        pw.flush();
294
                        pw.close();
279
                        pw.close();
(-)a/openide.modules/src/org/openide/modules/InstalledFileLocator.java (-3 / +34 lines)
Lines 43-48 Link Here
43
43
44
import java.io.File;
44
import java.io.File;
45
import java.util.Collection;
45
import java.util.Collection;
46
import java.util.Collections;
47
import java.util.LinkedHashSet;
48
import java.util.Set;
46
import org.openide.util.Lookup;
49
import org.openide.util.Lookup;
47
import org.openide.util.LookupEvent;
50
import org.openide.util.LookupEvent;
48
import org.openide.util.LookupListener;
51
import org.openide.util.LookupListener;
Lines 70-75 Link Here
70
            
73
            
71
            return null;
74
            return null;
72
        }
75
        }
76
        public @Override Set<File> locateAll(String relativePath, String codeNameBase, boolean localized) {
77
            Set<File> result = null;
78
            for (InstalledFileLocator ifl : getInstances()) {
79
                Set<File> added = ifl.locateAll(relativePath, codeNameBase, localized);
80
                // avoid allocating extra lists, under the assumption there is only one result:
81
                if (!added.isEmpty()) {
82
                    if (result == null) {
83
                        result = added;
84
                    } else {
85
                        result = new LinkedHashSet<File>(result);
86
                        result.addAll(added);
87
                    }
88
                }
89
            }
90
            return result != null ? result : Collections.<File>emptySet();
91
        }
73
    };
92
    };
74
    
93
    
75
    private static InstalledFileLocator[] instances = null;
94
    private static InstalledFileLocator[] instances = null;
Lines 108-116 Link Here
108
     * useful where a directory can contain many items that may be merged between e.g.
127
     * useful where a directory can contain many items that may be merged between e.g.
109
     * the installation and user directories. For example, the <samp>docs</samp> folder
128
     * the installation and user directories. For example, the <samp>docs</samp> folder
110
     * (used e.g. for Javadoc) might contain several ZIP files in both the installation and
129
     * (used e.g. for Javadoc) might contain several ZIP files in both the installation and
111
     * user areas. There is currently no supported way to enumerate all such files. Therefore
130
     * user areas. Use {@link #locateAll} if you need all results. The module may assume
112
     * searching for a directory should be attempted only when there is just one module which
113
     * is expected to provide that directory and all of its contents. The module may assume
114
     * that all contained files are in the same relative structure in the directory as in
131
     * that all contained files are in the same relative structure in the directory as in
115
     * the normal NBM-based installation; unusual locator implementations may need to create
132
     * the normal NBM-based installation; unusual locator implementations may need to create
116
     * temporary directories with matching structures to return from this method, in case the
133
     * temporary directories with matching structures to return from this method, in case the
Lines 182-187 Link Here
182
     * @return the requested <code>File</code>, if it can be found, else <code>null</code>
199
     * @return the requested <code>File</code>, if it can be found, else <code>null</code>
183
     */
200
     */
184
    public abstract File locate(String relativePath, String codeNameBase, boolean localized);
201
    public abstract File locate(String relativePath, String codeNameBase, boolean localized);
202
203
    /**
204
     * Similar to {@link #locate} but can return multiple results.
205
     * The default implementation returns a list with zero or one elements according to {@link #locate}.
206
     * @param relativePath a path from install root
207
     * @param codeNameBase name of the supplying module or null
208
     * @param localized true to perform a localized/branded search
209
     * @return a (possibly empty) set of files
210
     * @since XXX
211
     */
212
    public Set<File> locateAll(String relativePath, String codeNameBase, boolean localized) {
213
        File f = locate(relativePath, codeNameBase, localized);
214
        return f != null ? Collections.singleton(f) : Collections.<File>emptySet();
215
    }
185
    
216
    
186
    /**
217
    /**
187
     * Get a master locator.
218
     * Get a master locator.

Return to bug 113341