diff -r 7d779ce00a82 o.n.bootstrap/arch.xml
--- a/o.n.bootstrap/arch.xml Mon Apr 15 14:03:49 2013 +0200
+++ b/o.n.bootstrap/arch.xml Mon Apr 15 16:47:24 2013 +0200
@@ -1110,6 +1110,23 @@
purposes of installer.
+
Additional information about modules (like deprecation message, etc.)
diff -r 7d779ce00a82 o.n.bootstrap/src/org/netbeans/Clusters.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/o.n.bootstrap/src/org/netbeans/Clusters.java Mon Apr 15 16:47:24 2013 +0200
@@ -0,0 +1,165 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License. When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ *
+ * Contributor(s):
+ *
+ * Portions Copyrighted 2013 Sun Microsystems, Inc.
+ */
+
+package org.netbeans;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.StringTokenizer;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+final class Clusters implements Stamps.Updater {
+ private static String[] dirs;
+ private static String dirPrefix;
+ private static final Clusters INSTANCE = new Clusters();
+
+ private Clusters() {
+ }
+
+ static void scheduleSave(Stamps s) {
+ s.scheduleSaveImpl(INSTANCE, "all-clusters.dat", false); // NOI18N
+ }
+
+ static boolean compareDirs(DataInputStream is) throws IOException {
+ int cnt = is.readInt();
+ String[] arr = relativeDirsWithHome();
+ if (cnt != arr.length) {
+ return false;
+ }
+ for (int i = 0; i < arr.length; i++) {
+ String cluster = is.readUTF();
+ if (!cluster.equals(arr[i])) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ static synchronized String[] dirs() {
+ if (dirs == null) {
+ List tmp = new ArrayList();
+ String nbdirs = System.getProperty("netbeans.dirs");
+ if (nbdirs != null) {
+ StringTokenizer tok = new StringTokenizer(nbdirs, File.pathSeparator);
+ while (tok.hasMoreTokens()) {
+ tmp.add(tok.nextToken());
+ }
+ }
+ dirs = tmp.toArray(new String[tmp.size()]);
+ }
+ return dirs;
+ }
+
+ static int findCommonPrefix(String s1, String s2) {
+ int len = Math.min(s1.length(), s2.length());
+ int max = 0;
+ for (int i = 0; i < len; i++) {
+ final char ch = s1.charAt(i);
+ if (ch != s2.charAt(i)) {
+ return max;
+ }
+ if (ch == '/' || ch == File.separatorChar) {
+ max = i + 1;
+ }
+ }
+ return len;
+ }
+
+ static synchronized String dirPrefix() {
+ if (dirPrefix == null) {
+ String p = System.getProperty("netbeans.home");
+ for (String d : dirs()) {
+ if (p == null) {
+ p = d;
+ } else {
+ int len = findCommonPrefix(p, d);
+ if (len <= 3) {
+ p = "";
+ break;
+ }
+ p = p.substring(0, len);
+ }
+ }
+ dirPrefix = p == null ? "" : p;
+ }
+ return dirPrefix;
+ }
+
+ static String[] relativeDirsWithHome() {
+ String[] arr = dirs();
+ String[] tmp = new String[arr.length + 1];
+ tmp[0] = System.getProperty("netbeans.home", ""); // NOI18N
+ if (tmp[0].length() >= dirPrefix().length()) {
+ tmp[0] = tmp[0].substring(dirPrefix().length());
+ }
+ for (int i = 0; i < arr.length; i++) {
+ tmp[i + 1] = arr[i].substring(dirPrefix().length()).replace(File.separatorChar, '/');
+ }
+ return tmp;
+ }
+
+ static synchronized void clear() {
+ dirs = null;
+ dirPrefix = null;
+ }
+
+ @Override
+ public void flushCaches(DataOutputStream os) throws IOException {
+ String[] arr = relativeDirsWithHome();
+ os.writeInt(arr.length);
+ for (int i = 0; i < arr.length; i++) {
+ os.writeUTF(arr[i]);
+ }
+ }
+
+ @Override
+ public void cacheReady() {
+ }
+
+}
diff -r 7d779ce00a82 o.n.bootstrap/src/org/netbeans/Stamps.java
--- a/o.n.bootstrap/src/org/netbeans/Stamps.java Mon Apr 15 14:03:49 2013 +0200
+++ b/o.n.bootstrap/src/org/netbeans/Stamps.java Mon Apr 15 16:47:24 2013 +0200
@@ -45,6 +45,7 @@
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.DataInput;
+import java.io.DataInputStream;
import java.io.DataOutput;
import java.io.DataOutputStream;
import java.io.File;
@@ -68,7 +69,6 @@
import java.util.Locale;
import java.util.Random;
import java.util.Set;
-import java.util.StringTokenizer;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
@@ -91,9 +91,9 @@
private static final Logger LOG = Logger.getLogger(Stamps.class.getName());
private static AtomicLong moduleJARs;
private static File moduleNewestFile;
- private static String[] dirs;
private static File[] fallbackCache;
private static boolean populated;
+ private static Boolean clustersChanged;
private Worker worker = new Worker();
@@ -107,21 +107,24 @@
static void main(String... args) {
if (args.length == 1 && "reset".equals(args[0])) { // NOI18N
moduleJARs = null;
- dirs = null;
+ Clusters.clear();
+ clustersChanged = null;
fallbackCache = null;
stamp(false);
return;
}
if (args.length == 1 && "init".equals(args[0])) { // NOI18N
moduleJARs = null;
- dirs = null;
+ Clusters.clear();
+ clustersChanged = null;
fallbackCache = null;
stamp(true);
return;
}
if (args.length == 1 && "clear".equals(args[0])) { // NOI18N
moduleJARs = null;
- dirs = null;
+ Clusters.clear();
+ clustersChanged = null;
fallbackCache = null;
return;
}
@@ -183,6 +186,10 @@
return asByteBuffer(cache, true, false);
}
final File file(String cache, int[] len) {
+ if (clustersChanged()) {
+ return null;
+ }
+
checkPopulateCache();
synchronized (this) {
@@ -191,34 +198,7 @@
return null;
}
}
-
- File cacheFile = Places.getCacheSubfile(cache);
- long last = cacheFile.lastModified();
- if (last <= 0) {
- LOG.log(Level.FINE, "Cache does not exist when asking for {0}", cache); // NOI18N
- cacheFile = findFallbackCache(cache);
- if (cacheFile == null || (last = cacheFile.lastModified()) <= 0) {
- return null;
- }
- LOG.log(Level.FINE, "Found fallback cache at {0}", cacheFile);
- }
-
- if (moduleJARs() > last) {
- LOG.log(Level.FINE, "Timestamp does not pass when asking for {0}. Newest file {1}", new Object[] { cache, moduleNewestFile }); // NOI18N
- return null;
- }
-
- long longLen = cacheFile.length();
- if (longLen > Integer.MAX_VALUE) {
- LOG.warning("Cache file is too big: " + longLen + " bytes for " + cacheFile); // NOI18N
- return null;
- }
- if (len != null) {
- len[0] = (int)longLen;
- }
-
- LOG.log(Level.FINE, "Cache found: {0}", cache); // NOI18N
- return cacheFile;
+ return fileImpl(cache, len, moduleJARs());
}
private ByteBuffer asByteBuffer(String cache, boolean direct, boolean mmap) {
@@ -260,12 +240,17 @@
*/
public void scheduleSave(Updater updater, String cache, boolean append) {
boolean firstAdd;
- synchronized (worker) {
- firstAdd = worker.addStorage(new Store(updater, cache, append));
- }
+ firstAdd = scheduleSaveImpl(updater, cache, append);
LOG.log(firstAdd ? Level.FINE : Level.FINER,
"Scheduling save for {0} cache", cache
);
+ Clusters.scheduleSave(this);
+ }
+
+ final boolean scheduleSaveImpl(Updater updater, String cache, boolean append) {
+ synchronized (worker) {
+ return worker.addStorage(new Store(updater, cache, append));
+ }
}
/** Flushes all caches.
@@ -345,36 +330,23 @@
stamp(checkStampFile, result, newestFile);
return result;
}
-
- private static synchronized String[] dirs() {
- if (dirs == null) {
- List tmp = new ArrayList();
- String nbdirs = System.getProperty("netbeans.dirs"); // NOI18N
- if (nbdirs != null) {
- StringTokenizer tok = new StringTokenizer(nbdirs, File.pathSeparator);
- while (tok.hasMoreTokens()) {
- tmp.add(tok.nextToken());
- }
- }
- dirs = tmp.toArray(new String[tmp.size()]);
- }
- return dirs;
- }
private static void stamp(boolean checkStampFile, AtomicLong result, AtomicReference newestFile) {
StringBuilder sb = new StringBuilder();
Set processedDirs = new HashSet();
+ String[] relativeDirs = Clusters.relativeDirsWithHome();
String home = System.getProperty ("netbeans.home"); // NOI18N
if (home != null) {
long stamp = stampForCluster (new File (home), result, newestFile, processedDirs, checkStampFile, true, null);
- sb.append("home=").append(stamp).append('\n');
+ sb.append(relativeDirs[0]).append('=').append(stamp).append('\n');
}
- for (String t : dirs()) {
- final File clusterDir = new File(t);
+ String[] drs = Clusters.dirs();
+ for (int i = 0; i < drs.length; i++) {
+ final File clusterDir = new File(drs[i]);
long stamp = stampForCluster(clusterDir, result, newestFile, processedDirs, checkStampFile, true, null);
if (stamp != -1) {
- sb.append(clusterDir.getName()).append('=').append(stamp).append('\n');
+ sb.append("cluster.").append(relativeDirs[i + 1]).append('=').append(stamp).append('\n');
}
}
File user = Places.getUserDirectory();
@@ -561,8 +533,8 @@
private static File findFallbackCache(String cache) {
if (fallbackCache == null) {
fallbackCache = new File[0];
- if (dirs().length >= 1) {
- File fallback = new File(new File(new File(dirs()[0]), "var"), "cache"); // NOI18N
+ if (Clusters.dirs().length >= 1) {
+ File fallback = new File(new File(new File(Clusters.dirs()[0]), "var"), "cache"); // NOI18N
if (fallback.isDirectory()) {
fallbackCache = new File[]{ fallback };
}
@@ -620,6 +592,69 @@
LOG.log(Level.INFO, "Failed to populate {0}", cache);
}
}
+
+ private static boolean clustersChanged() {
+ if (clustersChanged != null) {
+ return clustersChanged;
+ }
+
+ final String clustersCache = "all-clusters.dat"; // NOI18N
+ File f = fileImpl(clustersCache, null, -1); // no timestamp check
+ if (f != null) {
+ DataInputStream dis = null;
+ try {
+ dis = new DataInputStream(new FileInputStream(f));
+ if (Clusters.compareDirs(dis)) {
+ return false;
+ }
+ } catch (IOException ex) {
+ return clustersChanged = true;
+ } finally {
+ if (dis != null) {
+ try {
+ dis.close();
+ } catch (IOException ex) {
+ LOG.log(Level.INFO, null, ex);
+ }
+ }
+ }
+ } else {
+ // missing cluster file signals caches are OK, for
+ // backward compatibility
+ return clustersChanged = false;
+ }
+ return clustersChanged = true;
+ }
+
+ private static File fileImpl(String cache, int[] len, long moduleJARs) {
+ File cacheFile = Places.getCacheSubfile(cache);
+ long last = cacheFile.lastModified();
+ if (last <= 0) {
+ LOG.log(Level.FINE, "Cache does not exist when asking for {0}", cache); // NOI18N
+ cacheFile = findFallbackCache(cache);
+ if (cacheFile == null || (last = cacheFile.lastModified()) <= 0) {
+ return null;
+ }
+ LOG.log(Level.FINE, "Found fallback cache at {0}", cacheFile);
+ }
+
+ if (moduleJARs > last) {
+ LOG.log(Level.FINE, "Timestamp does not pass when asking for {0}. Newest file {1}", new Object[] { cache, moduleNewestFile }); // NOI18N
+ return null;
+ }
+
+ long longLen = cacheFile.length();
+ if (longLen > Integer.MAX_VALUE) {
+ LOG.log(Level.WARNING, "Cache file is too big: {0} bytes for {1}", new Object[]{longLen, cacheFile}); // NOI18N
+ return null;
+ }
+ if (len != null) {
+ len[0] = (int)longLen;
+ }
+
+ LOG.log(Level.FINE, "Cache found: {0}", cache); // NOI18N
+ return cacheFile;
+ }
/** A callback interface to flush content of some cache at a suitable
* point in time.
@@ -908,7 +943,7 @@
return relative;
}
int indx = Integer.parseInt(index);
- String[] _dirs = dirs();
+ String[] _dirs = Clusters.dirs();
if (indx < 0 || indx >= _dirs.length) {
throw new IOException("Bad index " + indx + " for " + Arrays.toString(_dirs));
}
@@ -931,7 +966,7 @@
return;
}
int cnt = 0;
- for (String p : dirs()) {
+ for (String p : Clusters.dirs()) {
if (testWritePath(path, p, "" + cnt, out)) {
return;
}
diff -r 7d779ce00a82 o.n.bootstrap/test/unit/src/org/netbeans/ModuleManagerPersistanceTest.java
--- a/o.n.bootstrap/test/unit/src/org/netbeans/ModuleManagerPersistanceTest.java Mon Apr 15 14:03:49 2013 +0200
+++ b/o.n.bootstrap/test/unit/src/org/netbeans/ModuleManagerPersistanceTest.java Mon Apr 15 16:47:24 2013 +0200
@@ -72,7 +72,9 @@
clearWorkDir();
File home = new File(getWorkDir(), "home");
- new File(new File(home, "config"), "Modules").mkdirs();
+ final File configModules = new File(new File(home, "config"), "Modules");
+ configModules.mkdirs();
+ new File(configModules, "a-b-c.xml").createNewFile();
File moduleDir = new File(home, "modules");
moduleDir.mkdirs();
System.setProperty("netbeans.home", home.getPath());
diff -r 7d779ce00a82 o.n.bootstrap/test/unit/src/org/netbeans/StampsClusterMovedTest.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/o.n.bootstrap/test/unit/src/org/netbeans/StampsClusterMovedTest.java Mon Apr 15 16:47:24 2013 +0200
@@ -0,0 +1,187 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 1997-2010 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License. When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ *
+ * Contributor(s):
+ *
+ * Portions Copyrighted 2008 Sun Microsystems, Inc.
+ */
+
+package org.netbeans;
+
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.util.Arrays;
+import org.netbeans.junit.NbTestCase;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+public class StampsClusterMovedTest extends NbTestCase implements Stamps.Updater{
+ private File userdir;
+ private File ide;
+ private File platform;
+ private File install;
+ private File mainCluster;
+
+ public StampsClusterMovedTest(String testName) {
+ super(testName);
+ }
+
+ public void testMoveOfAClusterIsDetected() throws Exception {
+ clearWorkDir();
+
+ install = new File(getWorkDir(), "install");
+ platform = new File(install, "platform");
+ platform.mkdirs();
+ new File(platform, ".lastModified").createNewFile();
+ ide = new File(install, "ide");
+ ide.mkdirs();
+ new File(ide, ".lastModified").createNewFile();
+ mainCluster = new File(install, "extra");
+ mainCluster.mkdirs();
+ assertTrue("Extra cluster exists", mainCluster.isDirectory());
+ new File(mainCluster, ".lastModified").createNewFile();
+ userdir = new File(getWorkDir(), "tmp");
+
+ System.setProperty("netbeans.home", platform.getPath());
+ System.setProperty("netbeans.dirs", ide.getPath() + File.pathSeparator + mainCluster.getPath());
+ System.setProperty("netbeans.user", userdir.getPath());
+
+
+ Thread.sleep(500);
+ long between = System.currentTimeMillis();
+ Thread.sleep(500);
+
+
+ Stamps.main("init");
+
+ Stamps.getModulesJARs().scheduleSave(this, "test-cache", false);
+ Stamps.getModulesJARs().waitFor(true);
+ int[] arr = { 0 };
+ File f = Stamps.getModulesJARs().file("test-cache", arr);
+ assertNotNull("Cache found", f);
+ assertEquals("Stamps of caches shall be the same as stamps of .lastModified",
+ f.lastModified(), Stamps.moduleJARs()
+ );
+
+
+ Thread.sleep(500);
+
+ File subDir = new File(getWorkDir(), "subdir");
+ subDir.mkdirs();
+ final File newExtra = new File(subDir, mainCluster.getName());
+ boolean renRes = mainCluster.renameTo(newExtra);
+ assertTrue("Rename succeeded", renRes);
+ assertTrue("Extra renamed: " + newExtra, newExtra.isDirectory());
+
+ System.setProperty("netbeans.dirs", ide.getPath() + File.pathSeparator + newExtra.getPath());
+
+ Stamps.main("init");
+
+ assertNull("Cache invalidated as relative location of clusters changed",
+ Stamps.getModulesJARs().asByteBuffer("test-cache")
+ );
+ }
+
+ public void testChangeOfClustersIsDetectedInSharedConfig() throws Exception {
+ clearWorkDir();
+
+ install = new File(getWorkDir(), "install");
+ platform = new File(install, "platform");
+ platform.mkdirs();
+ new File(platform, ".lastModified").createNewFile();
+ ide = new File(install, "ide");
+ ide.mkdirs();
+ new File(ide, ".lastModified").createNewFile();
+ mainCluster = new File(install, "extra");
+ mainCluster.mkdirs();
+ assertTrue("Extra cluster exists", mainCluster.isDirectory());
+ new File(mainCluster, ".lastModified").createNewFile();
+ userdir = new File(getWorkDir(), "tmp");
+ userdir.mkdirs();
+
+ System.setProperty("netbeans.home", platform.getPath());
+ System.setProperty("netbeans.dirs", ide.getPath());
+ // generate the cache to mainCluster directory
+ System.setProperty("netbeans.user", mainCluster.getPath());
+
+
+ Thread.sleep(500);
+ long between = System.currentTimeMillis();
+ Thread.sleep(500);
+
+
+ Stamps.main("init");
+
+ Stamps.getModulesJARs().scheduleSave(this, "test-cache", false);
+ Stamps.getModulesJARs().waitFor(true);
+ int[] arr = { 0 };
+ File f = Stamps.getModulesJARs().file("test-cache", arr);
+ assertNotNull("Cache found", f);
+ assertEquals("Stamps of caches shall be the same as stamps of .lastModified",
+ f.lastModified(), Stamps.moduleJARs()
+ );
+
+ File lmdir = new File(new File(new File(mainCluster, "var"), "cache"), "lastModified");
+ assertTrue(lmdir + " is dir", lmdir.isDirectory());
+ lmdir.renameTo(new File(lmdir.getParentFile(), "ignore"));
+ assertFalse(lmdir + " is no longer dir", lmdir.isDirectory());
+
+ Thread.sleep(500);
+
+ System.setProperty("netbeans.user", userdir.getPath());
+ // use mainCluster as cluster
+ System.setProperty("netbeans.dirs", mainCluster.getPath() + File.pathSeparator + ide.getPath());
+
+ Stamps.main("init");
+
+ assertNull("Cache invalidated set of clusters changed",
+ Stamps.getModulesJARs().asByteBuffer("test-cache")
+ );
+ }
+
+ @Override
+ public void flushCaches(DataOutputStream os) throws IOException {
+ os.write(1);
+ }
+
+ @Override
+ public void cacheReady() {
+ }
+}
diff -r 7d779ce00a82 o.n.bootstrap/test/unit/src/org/netbeans/StampsIdeLessThanPlatformTest.java
--- a/o.n.bootstrap/test/unit/src/org/netbeans/StampsIdeLessThanPlatformTest.java Mon Apr 15 14:03:49 2013 +0200
+++ b/o.n.bootstrap/test/unit/src/org/netbeans/StampsIdeLessThanPlatformTest.java Mon Apr 15 16:47:24 2013 +0200
@@ -132,7 +132,7 @@
String[] seg = line.split("=");
assertEquals("There should be one = in the: " + line, 2, seg.length);
String s = seg[0];
- if (s.endsWith("home")) {
+ if (s.endsWith("platform")) {
assertEquals("Correct for platform: " + line, "60000", seg[1]);
check ++;
}