changeset 5276:36ef759dbfcd

Merge
author coffeys
date Wed, 29 Aug 2012 22:37:35 +0100
parents 5565b51a31e1 eaf58bd0f505
children 770fc16ac677
files
diffstat 9 files changed, 383 insertions(+), 136 deletions(-) [+]
line wrap: on
line diff
--- a/.hgtags	Thu Aug 16 10:35:35 2012 +0100
+++ b/.hgtags	Wed Aug 29 22:37:35 2012 +0100
@@ -199,3 +199,5 @@
 0ae89e53f5300da1961984a7d81c220c7cf717d7 jdk7u6-b23
 df945ef30444adf08f3ef14b0c49c8bda6dda587 jdk7u8-b01
 dd1e513c05b8b8c8402e9ecf9c0d5bdbebb1a089 jdk7u8-b02
+355cf1937d0824b54ac38ee5a5496197647840f9 jdk7u8-b03
+d099fc840e6c0f07e7b6f52d11fae98ae217bc9a jdk7u8-b04
--- a/src/macosx/classes/java/util/prefs/MacOSXPreferences.java	Thu Aug 16 10:35:35 2012 +0100
+++ b/src/macosx/classes/java/util/prefs/MacOSXPreferences.java	Wed Aug 29 22:37:35 2012 +0100
@@ -33,16 +33,16 @@
     private static final String defaultAppName = "com.apple.java.util.prefs";
 
     // true if this node is a child of userRoot or is userRoot
-    private boolean isUser;
+    private final boolean isUser;
 
     // true if this node is userRoot or systemRoot
-    private boolean isRoot;
+    private final boolean isRoot;
 
     // CF's storage location for this node and its keys
-    private MacOSXPreferencesFile file;
+    private final MacOSXPreferencesFile file;
 
     // absolutePath() + "/"
-    private String path;
+    private final String path;
 
     // User root and system root nodes
     private static MacOSXPreferences userRoot = null;
@@ -71,36 +71,40 @@
 
     // Create a new root node. Called by getUserRoot() and getSystemRoot()
     // Synchronization is provided by the caller.
-    private MacOSXPreferences(boolean newIsUser)
-    {
-        super(null, "");
-        isUser = newIsUser;
-        isRoot = true;
-
-        initFields();
+    private MacOSXPreferences(boolean newIsUser) {
+        this(null, "", false, true, newIsUser);
     }
 
 
     // Create a new non-root node with the given parent.
     // Called by childSpi().
-    private MacOSXPreferences(MacOSXPreferences parent, String name)
+    private MacOSXPreferences(MacOSXPreferences parent, String name) {
+        this(parent, name, false, false, false);
+    }
+
+    private MacOSXPreferences(MacOSXPreferences parent, String name,
+                              boolean isNew)
+    {
+        this(parent, name, isNew, false, false);
+    }
+
+    private MacOSXPreferences(MacOSXPreferences parent, String name,
+                              boolean isNew, boolean isRoot, boolean isUser)
     {
         super(parent, name);
-        isUser = isUserNode();
-        isRoot = false;
-
-        initFields();
-    }
-
-
-    private void initFields()
-    {
+        this.isRoot = isRoot;
+        if (isRoot)
+            this.isUser = isUser;
+        else
+            this.isUser = isUserNode();
         path = isRoot ? absolutePath() : absolutePath() + "/";
         file = cfFileForNode(isUser);
-        newNode = file.addNode(path);
+        if (isNew)
+            newNode = isNew;
+        else
+            newNode = file.addNode(path);
     }
 
-
     // Create and return the MacOSXPreferencesFile for this node.
     // Does not write anything to the file.
     private MacOSXPreferencesFile cfFileForNode(boolean isUser)
@@ -194,8 +198,8 @@
         // Add to parent's child list here and disallow sync
         // because parent and child might be in different files.
         synchronized(MacOSXPreferencesFile.class) {
-            file.addChildToNode(path, name);
-            return new MacOSXPreferences(this, name);
+            boolean isNew = file.addChildToNode(path, name);
+            return new MacOSXPreferences(this, name, isNew);
         }
     }
 
@@ -206,9 +210,14 @@
         // Flush should *not* check for removal, unlike sync, but should
         // prevent simultaneous removal.
         synchronized(lock) {
-            // fixme! overkill
-            if (!MacOSXPreferencesFile.flushWorld()) {
-                throw new BackingStoreException("Synchronization failed for node '" + path + "'");
+            if (isUser) {
+                if (!MacOSXPreferencesFile.flushUser()) {
+                    throw new BackingStoreException("Synchronization failed for node '" + path + "'");
+                }
+            } else {
+                if (!MacOSXPreferencesFile.flushWorld()) {
+                    throw new BackingStoreException("Synchronization failed for node '" + path + "'");
+                }
             }
         }
     }
--- a/src/macosx/classes/java/util/prefs/MacOSXPreferencesFile.java	Thu Aug 16 10:35:35 2012 +0100
+++ b/src/macosx/classes/java/util/prefs/MacOSXPreferencesFile.java	Wed Aug 29 22:37:35 2012 +0100
@@ -223,7 +223,23 @@
         return ok;
     }
 
-
+    //Flush only current user preferences
+    static synchronized boolean flushUser() {
+        boolean ok = true;
+        if (changedFiles != null  &&  !changedFiles.isEmpty()) {
+            Iterator<MacOSXPreferencesFile> iterator = changedFiles.iterator();
+            while(iterator.hasNext()) {
+                MacOSXPreferencesFile f = iterator.next();
+                if (f.user == cfCurrentUser) {
+                    if (!f.synchronize())
+                        ok = false;
+                    else
+                        iterator.remove();
+                }
+            }
+        }
+        return ok;
+    }
 
     // Write all prefs changes to disk, but do not clear all cached prefs
     // values. Also kills any scheduled flush task.
@@ -348,11 +364,11 @@
         }
     }
 
-    void addChildToNode(String path, String child)
+    boolean addChildToNode(String path, String child)
     {
         synchronized(MacOSXPreferencesFile.class) {
             markChanged();
-            addChildToNode(path, child+"/", appName, user, host);
+            return addChildToNode(path, child+"/", appName, user, host);
         }
     }
 
@@ -421,7 +437,7 @@
         addNode(String path, String name, long user, long host);
     private static final native void
         removeNode(String path, String name, long user, long host);
-    private static final native void
+    private static final native boolean
         addChildToNode(String path, String child,
                        String name, long user, long host);
     private static final native void
--- a/src/macosx/native/java/util/MacOSXPreferencesFile.m	Thu Aug 16 10:35:35 2012 +0100
+++ b/src/macosx/native/java/util/MacOSXPreferencesFile.m	Wed Aug 29 22:37:35 2012 +0100
@@ -641,7 +641,7 @@
 
 
 // child must end with '/'
-JNIEXPORT void JNICALL
+JNIEXPORT Boolean JNICALL
 Java_java_util_prefs_MacOSXPreferencesFile_addChildToNode
 (JNIEnv *env, jobject klass, jobject jpath, jobject jchild,
  jobject jname, jlong juser, jlong jhost)
@@ -656,6 +656,7 @@
     CFDictionaryRef node;
     CFStringRef topKey;
     CFMutableDictionaryRef topValue;
+    Boolean beforeAdd = false;
 
     if (!path  ||  !child  ||  !name) goto badparams;
 
@@ -665,9 +666,12 @@
     // copyMutableNode creates the node if necessary
     parent = copyMutableNode(path, name, user, host, &topKey, &topValue);
     throwIfNull(parent, "copyMutableNode failed");
-
+    beforeAdd = CFDictionaryContainsKey(parent, child);
     CFDictionaryAddValue(parent, child, node);
-
+    if (!beforeAdd)
+        beforeAdd = CFDictionaryContainsKey(parent, child);
+    else
+        beforeAdd = false;
     CFPreferencesSetValue(topKey, topValue, name, user, host);
 
     CFRelease(parent);
@@ -680,6 +684,7 @@
     if (path) CFRelease(path);
     if (child) CFRelease(child);
     if (name) CFRelease(name);
+    return beforeAdd;
 }
 
 
--- a/src/share/classes/sun/net/spi/DefaultProxySelector.java	Thu Aug 16 10:35:35 2012 +0100
+++ b/src/share/classes/sun/net/spi/DefaultProxySelector.java	Wed Aug 29 22:37:35 2012 +0100
@@ -346,5 +346,5 @@
     }
 
     private native static boolean init();
-    private native Proxy getSystemProxy(String protocol, String host);
+    private synchronized native Proxy getSystemProxy(String protocol, String host);
 }
--- a/src/share/classes/sun/security/krb5/Config.java	Thu Aug 16 10:35:35 2012 +0100
+++ b/src/share/classes/sun/security/krb5/Config.java	Wed Aug 29 22:37:35 2012 +0100
@@ -116,7 +116,12 @@
 
     private static boolean isMacosLionOrBetter() {
         // split the "10.x.y" version number
-        String osVersion = System.getProperty("os.version");
+        String osname = getProperty("os.name");
+        if (!osname.contains("OS X")) {
+            return false;
+        }
+
+        String osVersion = getProperty("os.version");
         String[] fragments = osVersion.split("\\.");
 
         // sanity check the "10." part of the version
@@ -141,20 +146,14 @@
         /*
          * If either one system property is specified, we throw exception.
          */
-        String tmp =
-            java.security.AccessController.doPrivileged(
-                new sun.security.action.GetPropertyAction
-                    ("java.security.krb5.kdc"));
+        String tmp = getProperty("java.security.krb5.kdc");
         if (tmp != null) {
             // The user can specify a list of kdc hosts separated by ":"
             defaultKDC = tmp.replace(':', ' ');
         } else {
             defaultKDC = null;
         }
-        defaultRealm =
-            java.security.AccessController.doPrivileged(
-                new sun.security.action.GetPropertyAction
-                    ("java.security.krb5.realm"));
+        defaultRealm = getProperty("java.security.krb5.realm");
         if ((defaultKDC == null && defaultRealm != null) ||
             (defaultRealm == null && defaultKDC != null)) {
             throw new KrbException
@@ -166,11 +165,34 @@
         // Always read the Kerberos configuration file
         try {
             Vector<String> configFile;
-            configFile = loadConfigFile();
-            if (configFile == null && isMacosLionOrBetter()) {
-                stanzaTable = SCDynamicStoreConfig.getConfig();
+            String fileName = getJavaFileName();
+            if (fileName != null) {
+                configFile = loadConfigFile(fileName);
+                stanzaTable = parseStanzaTable(configFile);
+                if (DEBUG) {
+                    System.out.println("Loaded from Java config");
+                }
             } else {
-                stanzaTable = parseStanzaTable(configFile);
+                boolean found = false;
+                if (isMacosLionOrBetter()) {
+                    try {
+                        stanzaTable = SCDynamicStoreConfig.getConfig();
+                        if (DEBUG) {
+                            System.out.println("Loaded from SCDynamicStoreConfig");
+                        }
+                        found = true;
+                    } catch (IOException ioe) {
+                        // OK. Will go on with file
+                    }
+                }
+                if (!found) {
+                    fileName = getNativeFileName();
+                    configFile = loadConfigFile(fileName);
+                    stanzaTable = parseStanzaTable(configFile);
+                    if (DEBUG) {
+                        System.out.println("Loaded from native config");
+                    }
+                }
             }
         } catch (IOException ioe) {
             // No krb5.conf, no problem. We'll use DNS or system property etc.
@@ -538,10 +560,13 @@
      * [domain_realm]
      *          blue.sample.com = TEST.SAMPLE.COM
      *          .backup.com     = EXAMPLE.COM
+     *
+     * @params fileName the conf file, cannot be null
+     * @return the content, null if fileName is empty
+     * @throws IOException if there is an I/O or format error
      */
-    private Vector<String> loadConfigFile() throws IOException {
+    private Vector<String> loadConfigFile(final String fileName) throws IOException {
         try {
-            final String fileName = getFileName();
             if (!fileName.equals("")) {
                 BufferedReader br = new BufferedReader(new InputStreamReader(
                 java.security.AccessController.doPrivileged(
@@ -660,97 +685,106 @@
     }
 
     /**
-     * Gets the default configuration file name. This method will never
-     * return null.
+     * Gets the default Java configuration file name.
      *
      * If the system property "java.security.krb5.conf" is defined, we'll
-     * use its value, no matter if the file exists or not. Otherwise,
-     * the file will be searched in a list of possible loations in the
-     * following order:
+     * use its value, no matter if the file exists or not. Otherwise, we
+     * will look at $JAVA_HOME/lib/security directory with "krb5.conf" name,
+     * and return it if the file exists.
      *
-     * 1. at Java home lib\security directory with "krb5.conf" name,
-     * 2. at windows directory with the name of "krb5.ini" for Windows,
-     * /etc/krb5/krb5.conf for Solaris, /etc/krb5.conf otherwise.
+     * The method returns null if it cannot find a Java config file.
+     */
+    private String getJavaFileName() {
+        String name = getProperty("java.security.krb5.conf");
+        if (name == null) {
+            name = getProperty("java.home") + File.separator +
+                                "lib" + File.separator + "security" +
+                                File.separator + "krb5.conf";
+            if (!fileExists(name)) {
+                name = null;
+            }
+        }
+        if (DEBUG) {
+            System.out.println("Java config name: " + name);
+        }
+        return name;
+    }
+
+    /**
+     * Gets the default native configuration file name.
+     *
+     * Depending on the OS type, the method returns the default native
+     * kerberos config file name, which is at windows directory with
+     * the name of "krb5.ini" for Windows, /etc/krb5/krb5.conf for Solaris,
+     * /etc/krb5.conf otherwise. Mac OSX X has a different file name.
      *
      * Note: When the Terminal Service is started in Windows (from 2003),
      * there are two kinds of Windows directories: A system one (say,
      * C:\Windows), and a user-private one (say, C:\Users\Me\Windows).
      * We will first look for krb5.ini in the user-private one. If not
      * found, try the system one instead.
+     *
+     * This method will always return a non-null non-empty file name,
+     * even if that file does not exist.
      */
-    private String getFileName() {
-        String name =
-            java.security.AccessController.doPrivileged(
-                                new sun.security.action.
-                                GetPropertyAction("java.security.krb5.conf"));
-        if (name == null) {
-            name = java.security.AccessController.doPrivileged(
-                        new sun.security.action.
-                        GetPropertyAction("java.home")) + File.separator +
-                                "lib" + File.separator + "security" +
-                                File.separator + "krb5.conf";
-            if (!fileExists(name)) {
-                name = null;
-                String osname =
-                        java.security.AccessController.doPrivileged(
-                        new sun.security.action.GetPropertyAction("os.name"));
-                if (osname.startsWith("Windows")) {
-                    try {
-                        Credentials.ensureLoaded();
-                    } catch (Exception e) {
-                        // ignore exceptions
+    private String getNativeFileName() {
+        String name = null;
+        String osname = getProperty("os.name");
+        if (osname.startsWith("Windows")) {
+            try {
+                Credentials.ensureLoaded();
+            } catch (Exception e) {
+                // ignore exceptions
+            }
+            if (Credentials.alreadyLoaded) {
+                String path = getWindowsDirectory(false);
+                if (path != null) {
+                    if (path.endsWith("\\")) {
+                        path = path + "krb5.ini";
+                    } else {
+                        path = path + "\\krb5.ini";
                     }
-                    if (Credentials.alreadyLoaded) {
-                        String path = getWindowsDirectory(false);
-                        if (path != null) {
-                            if (path.endsWith("\\")) {
-                                path = path + "krb5.ini";
-                            } else {
-                                path = path + "\\krb5.ini";
-                            }
-                            if (fileExists(path)) {
-                                name = path;
-                            }
+                    if (fileExists(path)) {
+                        name = path;
+                    }
+                }
+                if (name == null) {
+                    path = getWindowsDirectory(true);
+                    if (path != null) {
+                        if (path.endsWith("\\")) {
+                            path = path + "krb5.ini";
+                        } else {
+                            path = path + "\\krb5.ini";
                         }
-                        if (name == null) {
-                            path = getWindowsDirectory(true);
-                            if (path != null) {
-                                if (path.endsWith("\\")) {
-                                    path = path + "krb5.ini";
-                                } else {
-                                    path = path + "\\krb5.ini";
-                                }
-                                name = path;
-                            }
-                        }
+                        name = path;
                     }
-                    if (name == null) {
-                        name = "c:\\winnt\\krb5.ini";
-                    }
-                } else if (osname.startsWith("SunOS")) {
-                    name =  "/etc/krb5/krb5.conf";
-                } else if (osname.contains("OS X")) {
-                    if (isMacosLionOrBetter()) return "";
-                    name = findMacosConfigFile();
-                } else {
-                    name =  "/etc/krb5.conf";
                 }
             }
+            if (name == null) {
+                name = "c:\\winnt\\krb5.ini";
+            }
+        } else if (osname.startsWith("SunOS")) {
+            name =  "/etc/krb5/krb5.conf";
+        } else if (osname.contains("OS X")) {
+            name = findMacosConfigFile();
+        } else {
+            name =  "/etc/krb5.conf";
         }
         if (DEBUG) {
-            System.out.println("Config name: " + name);
+            System.out.println("Native config name: " + name);
         }
         return name;
     }
 
-    private String getProperty(String property) {
-        return (String)java.security.AccessController.doPrivileged(new sun.security.action.GetPropertyAction(property));
+    private static String getProperty(String property) {
+        return java.security.AccessController.doPrivileged(
+                new sun.security.action.GetPropertyAction(property));
     }
 
     private String findMacosConfigFile() {
         String userHome = getProperty("user.home");
         final String PREF_FILE = "/Library/Preferences/edu.mit.Kerberos";
-        String userPrefs=userHome + PREF_FILE;
+        String userPrefs = userHome + PREF_FILE;
 
         if (fileExists(userPrefs)) {
             return userPrefs;
@@ -760,11 +794,7 @@
             return PREF_FILE;
         }
 
-        if (fileExists("/etc/krb5.conf")) {
-            return "/etc/krb5.conf";
-        }
-
-        return "";
+        return "/etc/krb5.conf";
     }
 
     private static String trimmed(String s) {
@@ -1334,32 +1364,52 @@
         }
     }
 
+    // Shows the content of the Config object for debug purpose.
+    //
+    // {
+    //      libdefaults = {
+    //          default_realm = R
+    //      }
+    //      realms = {
+    //          R = {
+    //              kdc = [k1,k2]
+    //          }
+    //      }
+    // }
+
     @Override
     public String toString() {
         StringBuffer sb = new StringBuffer();
-        toStringIndented("", stanzaTable, sb);
+        toStringInternal("", stanzaTable, sb);
         return sb.toString();
     }
-    private static void toStringIndented(String prefix, Object obj,
+    private static void toStringInternal(String prefix, Object obj,
             StringBuffer sb) {
         if (obj instanceof String) {
-            sb.append(prefix);
-            sb.append(obj);
-            sb.append('\n');
+            // A string value, just print it
+            sb.append(obj).append('\n');
         } else if (obj instanceof Hashtable) {
-            Hashtable tab = (Hashtable)obj;
+            // A table, start a new sub-section...
+            Hashtable<?, ?> tab = (Hashtable<?, ?>)obj;
+            sb.append("{\n");
             for (Object o: tab.keySet()) {
-                sb.append(prefix);
+                // ...indent, print "key = ", and
+                sb.append(prefix).append("    ").append(o).append(" = ");
+                // ...go recursively into value
+                toStringInternal(prefix + "    ", tab.get(o), sb);
+            }
+            sb.append(prefix).append("}\n");
+        } else if (obj instanceof Vector) {
+            // A vector of strings, print them inside [ and ]
+            Vector<?> v = (Vector<?>)obj;
+            sb.append("[");
+            boolean first = true;
+            for (Object o: v.toArray()) {
+                if (!first) sb.append(",");
                 sb.append(o);
-                sb.append(" = {\n");
-                toStringIndented(prefix + "    ", tab.get(o), sb);
-                sb.append(prefix + "}\n");
+                first = false;
             }
-        } else if (obj instanceof Vector) {
-            Vector v = (Vector)obj;
-            for (Object o: v.toArray()) {
-                toStringIndented(prefix + "    ", o, sb);
-            }
+            sb.append("]\n");
         }
     }
 }
--- a/src/share/native/sun/awt/medialib/mlib_types.h	Thu Aug 16 10:35:35 2012 +0100
+++ b/src/share/native/sun/awt/medialib/mlib_types.h	Wed Aug 29 22:37:35 2012 +0100
@@ -59,8 +59,16 @@
 
 #if defined(__SUNPRO_C) || defined(__SUNPRO_CC) || defined(__GNUC__)
 
+#if defined(MACOSX)
 #include <stddef.h>                     /* for ptrdiff_t */
 #include <stdint.h>                     /* for uintptr_t */
+#elif defined(__linux__)
+#include <stdint.h>                     /* for uintptr_t */
+#include <malloc.h>                     /* for ptrdiff_t */
+#else
+#include <link.h>                       /* for uintptr_t */
+#include <stddef.h>                     /* for ptrdiff_t */
+#endif  /* __linux__ */
 
 #if defined(MLIB_OS64BIT) || (defined(MACOSX) && defined(_LP64))
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/java/net/ProxySelector/MultiThreadedSystemProxies.java	Wed Aug 29 22:37:35 2012 +0100
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2012 Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+/*
+ * @test
+ * @bug 7188755
+ * @run main/othervm MultiThreadedSystemProxies
+ * @summary Crash due to missing synchronization on gconf_client in
+ *          DefaultProxySelector.c
+ */
+import java.net.ProxySelector;
+import java.net.URI;
+
+/* Racey test, not guaranteed to fail, but if it does we have a problem. */
+
+public class MultiThreadedSystemProxies {
+    static final int NUM_THREADS = 100;
+
+    public static void main(String[] args) throws Exception {
+        System.setProperty("java.net.useSystemProxies", "true");
+        final ProxySelector ps = ProxySelector.getDefault();
+        final URI uri = new URI("http://ubuntu.com");
+        Thread[] threads = new Thread[NUM_THREADS];
+
+        for (int i = 0; i < NUM_THREADS; i++) {
+            threads[i] = new Thread(new Runnable() {
+                @Override
+                public void run() {
+                    try {
+                        ps.select(uri);
+                    } catch (Exception x) {
+                        throw new RuntimeException(x);
+                    }
+                }
+            });
+        }
+        for (int i = 0; i < NUM_THREADS; i++) {
+            threads[i].start();
+        }
+        for (int i = 0; i < NUM_THREADS; i++) {
+            threads[i].join();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/java/util/prefs/AddNodeChangeListener.java	Wed Aug 29 22:37:35 2012 +0100
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+ /* @test
+  * @bug  7160252
+  * @summary Checks if events are delivered to a listener
+  *          when a child node is added or removed
+  */
+
+import java.util.prefs.*;
+
+ public class AddNodeChangeListener {
+
+     private static boolean failed = false;
+     private static Preferences userRoot, N2;
+     private static NodeChangeListenerAdd ncla;
+
+     public static void main(String[] args)
+         throws BackingStoreException, InterruptedException
+     {
+        userRoot = Preferences.userRoot();
+        ncla = new NodeChangeListenerAdd();
+        userRoot.addNodeChangeListener(ncla);
+        //Should initiate a node added event
+        addNode();
+        // Should not initiate a node added event
+        addNode();
+        //Should initate a child removed event
+        removeNode();
+
+        if (failed)
+            throw new RuntimeException("Failed");
+    }
+
+    private static void addNode()
+        throws BackingStoreException, InterruptedException
+    {
+        N2 = userRoot.node("N2");
+        userRoot.flush();
+        Thread.sleep(3000);
+        if (ncla.getAddNumber() != 1)
+            failed = true;
+    }
+
+    private static void removeNode()
+        throws BackingStoreException, InterruptedException
+    {
+        N2.removeNode();
+        userRoot.flush();
+        Thread.sleep(3000);
+        if (ncla.getAddNumber() != 0)
+            failed = true;
+    }
+
+    private static class NodeChangeListenerAdd implements NodeChangeListener {
+        private int totalNode = 0;
+
+        @Override
+        public void childAdded(NodeChangeEvent evt) {
+            totalNode++;
+        }
+
+        @Override
+        public void childRemoved(NodeChangeEvent evt) {
+            totalNode--;
+        }
+
+        public int getAddNumber(){
+            return totalNode;
+        }
+    }
+ }