Merge
authortbell
Fri, 03 Apr 2009 10:29:13 -0700
changeset 2440 79e224b63d3c
parent 2367 4ac66229737e (current diff)
parent 2439 16036409c484 (diff)
child 2447 ebdc868a34d7
Merge
--- a/jdk/src/share/classes/java/lang/Integer.java	Thu Apr 02 16:51:55 2009 -0700
+++ b/jdk/src/share/classes/java/lang/Integer.java	Fri Apr 03 10:29:13 2009 -0700
@@ -25,6 +25,8 @@
 
 package java.lang;
 
+import java.util.Properties;
+
 /**
  * The {@code Integer} class wraps a value of the primitive type
  * {@code int} in an object. An object of type {@code Integer}
@@ -442,6 +444,12 @@
     public static int parseInt(String s, int radix)
                 throws NumberFormatException
     {
+        /*
+         * WARNING: This method may be invoked early during VM initialization
+         * before IntegerCache is initialized. Care must be taken to not use
+         * the valueOf method.
+         */
+
         if (s == null) {
             throw new NumberFormatException("null");
         }
@@ -545,7 +553,7 @@
      *            does not contain a parsable {@code int}.
      */
     public static Integer valueOf(String s, int radix) throws NumberFormatException {
-        return new Integer(parseInt(s,radix));
+        return Integer.valueOf(parseInt(s,radix));
     }
 
     /**
@@ -570,20 +578,56 @@
      * @exception  NumberFormatException  if the string cannot be parsed
      *             as an integer.
      */
-    public static Integer valueOf(String s) throws NumberFormatException
-    {
-        return new Integer(parseInt(s, 10));
+    public static Integer valueOf(String s) throws NumberFormatException {
+        return Integer.valueOf(parseInt(s, 10));
+    }
+
+    /**
+     * Cache to support the object identity semantics of autoboxing for values between
+     * -128 and 127 (inclusive) as required by JLS.
+     *
+     * The cache is initialized on first usage. During VM initialization the
+     * getAndRemoveCacheProperties method may be used to get and remove any system
+     * properites that configure the cache size. At this time, the size of the
+     * cache may be controlled by the -XX:AutoBoxCacheMax=<size> option.
+     */
+
+    // value of java.lang.Integer.IntegerCache.high property (obtained during VM init)
+    private static String integerCacheHighPropValue;
+
+    static void getAndRemoveCacheProperties() {
+        if (!sun.misc.VM.isBooted()) {
+            Properties props = System.getProperties();
+            integerCacheHighPropValue =
+                (String)props.remove("java.lang.Integer.IntegerCache.high");
+            if (integerCacheHighPropValue != null)
+                System.setProperties(props);  // remove from system props
+        }
     }
 
     private static class IntegerCache {
-        private IntegerCache(){}
-
-        static final Integer cache[] = new Integer[-(-128) + 127 + 1];
+        static final int low = -128;
+        static final int high;
+        static final Integer cache[];
 
         static {
-            for(int i = 0; i < cache.length; i++)
-                cache[i] = new Integer(i - 128);
+            // high value may be configured by property
+            int h = 127;
+            if (integerCacheHighPropValue != null) {
+                int i = parseInt(integerCacheHighPropValue);
+                i = Math.max(i, 127);
+                // Maximum array size is Integer.MAX_VALUE
+                h = Math.min(i, Integer.MAX_VALUE - (-low));
+            }
+            high = h;
+
+            cache = new Integer[(high - low) + 1];
+            int j = low;
+            for(int k = 0; k < cache.length; k++)
+                cache[k] = new Integer(j++);
         }
+
+        private IntegerCache() {}
     }
 
     /**
@@ -599,10 +643,9 @@
      * @since  1.5
      */
     public static Integer valueOf(int i) {
-        final int offset = 128;
-        if (i >= -128 && i <= 127) { // must cache
-            return IntegerCache.cache[i + offset];
-        }
+        assert IntegerCache.high >= 127;
+        if (i >= IntegerCache.low && i <= IntegerCache.high)
+            return IntegerCache.cache[i + (-IntegerCache.low)];
         return new Integer(i);
     }
 
@@ -806,7 +849,7 @@
      */
     public static Integer getInteger(String nm, int val) {
         Integer result = getInteger(nm, null);
-        return (result == null) ? new Integer(val) : result;
+        return (result == null) ? Integer.valueOf(val) : result;
     }
 
     /**
@@ -938,7 +981,7 @@
 
         try {
             result = Integer.valueOf(nm.substring(index), radix);
-            result = negative ? new Integer(-result.intValue()) : result;
+            result = negative ? Integer.valueOf(-result.intValue()) : result;
         } catch (NumberFormatException e) {
             // If number is Integer.MIN_VALUE, we'll end up here. The next line
             // handles this case, and causes any genuine format error to be
--- a/jdk/src/share/classes/java/lang/Long.java	Thu Apr 02 16:51:55 2009 -0700
+++ b/jdk/src/share/classes/java/lang/Long.java	Fri Apr 03 10:29:13 2009 -0700
@@ -510,7 +510,7 @@
      *             contain a parsable {@code long}.
      */
     public static Long valueOf(String s, int radix) throws NumberFormatException {
-        return new Long(parseLong(s, radix));
+        return Long.valueOf(parseLong(s, radix));
     }
 
     /**
@@ -537,7 +537,7 @@
      */
     public static Long valueOf(String s) throws NumberFormatException
     {
-        return new Long(parseLong(s, 10));
+        return Long.valueOf(parseLong(s, 10));
     }
 
     private static class LongCache {
@@ -650,7 +650,7 @@
 
         try {
             result = Long.valueOf(nm.substring(index), radix);
-            result = negative ? new Long(-result.longValue()) : result;
+            result = negative ? Long.valueOf(-result.longValue()) : result;
         } catch (NumberFormatException e) {
             // If number is Long.MIN_VALUE, we'll end up here. The next line
             // handles this case, and causes any genuine format error to be
@@ -869,7 +869,7 @@
      */
     public static Long getLong(String nm, long val) {
         Long result = Long.getLong(nm, null);
-        return (result == null) ? new Long(val) : result;
+        return (result == null) ? Long.valueOf(val) : result;
     }
 
     /**
--- a/jdk/src/share/classes/java/lang/System.java	Thu Apr 02 16:51:55 2009 -0700
+++ b/jdk/src/share/classes/java/lang/System.java	Fri Apr 03 10:29:13 2009 -0700
@@ -1105,6 +1105,13 @@
         props = new Properties();
         initProperties(props);
         sun.misc.Version.init();
+
+        // Gets and removes system properties that configure the Integer
+        // cache used to support the object identity semantics of autoboxing.
+        // At this time, the size of the cache may be controlled by the
+        // -XX:AutoBoxCacheMax=<size> option.
+        Integer.getAndRemoveCacheProperties();
+
         FileInputStream fdIn = new FileInputStream(FileDescriptor.in);
         FileOutputStream fdOut = new FileOutputStream(FileDescriptor.out);
         FileOutputStream fdErr = new FileOutputStream(FileDescriptor.err);
--- a/jdk/src/share/classes/java/nio/channels/DatagramChannel.java	Thu Apr 02 16:51:55 2009 -0700
+++ b/jdk/src/share/classes/java/nio/channels/DatagramChannel.java	Fri Apr 03 10:29:13 2009 -0700
@@ -261,7 +261,10 @@
      *
      * <p> This method may be invoked at any time.  It will not have any effect
      * on read or write operations that are already in progress at the moment
-     * that it is invoked.  </p>
+     * that it is invoked. If this channel's socket is not bound then this method
+     * will first cause the socket to be bound to an address that is assigned
+     * automatically, as if invoking the {@link #bind bind} method with a
+     * parameter of {@code null}. </p>
      *
      * @param  remote
      *         The remote address to which this channel is to be connected
@@ -356,7 +359,10 @@
      * <p> This method may be invoked at any time.  If another thread has
      * already initiated a read operation upon this channel, however, then an
      * invocation of this method will block until the first operation is
-     * complete. </p>
+     * complete. If this channel's socket is not bound then this method will
+     * first cause the socket to be bound to an address that is assigned
+     * automatically, as if invoking the {@link #bind bind} method with a
+     * parameter of {@code null}. </p>
      *
      * @param  dst
      *         The buffer into which the datagram is to be transferred
@@ -413,7 +419,10 @@
      * <p> This method may be invoked at any time.  If another thread has
      * already initiated a write operation upon this channel, however, then an
      * invocation of this method will block until the first operation is
-     * complete. </p>
+     * complete. If this channel's socket is not bound then this method will
+     * first cause the socket to be bound to an address that is assigned
+     * automatically, as if by invoking the {@link #bind bind) method with a
+     * parameter of {@code null}. </p>
      *
      * @param  src
      *         The buffer containing the datagram to be sent
--- a/jdk/src/share/classes/java/util/TreeMap.java	Thu Apr 02 16:51:55 2009 -0700
+++ b/jdk/src/share/classes/java/util/TreeMap.java	Fri Apr 03 10:29:13 2009 -0700
@@ -1068,14 +1068,14 @@
         }
         public NavigableSet<E> subSet(E fromElement, boolean fromInclusive,
                                       E toElement,   boolean toInclusive) {
-            return new TreeSet<E>(m.subMap(fromElement, fromInclusive,
-                                           toElement,   toInclusive));
+            return new KeySet<E>(m.subMap(fromElement, fromInclusive,
+                                          toElement,   toInclusive));
         }
         public NavigableSet<E> headSet(E toElement, boolean inclusive) {
-            return new TreeSet<E>(m.headMap(toElement, inclusive));
+            return new KeySet<E>(m.headMap(toElement, inclusive));
         }
         public NavigableSet<E> tailSet(E fromElement, boolean inclusive) {
-            return new TreeSet<E>(m.tailMap(fromElement, inclusive));
+            return new KeySet<E>(m.tailMap(fromElement, inclusive));
         }
         public SortedSet<E> subSet(E fromElement, E toElement) {
             return subSet(fromElement, true, toElement, false);
@@ -1087,7 +1087,7 @@
             return tailSet(fromElement, true);
         }
         public NavigableSet<E> descendingSet() {
-            return new TreeSet(m.descendingMap());
+            return new KeySet(m.descendingMap());
         }
     }
 
--- a/jdk/src/share/classes/java/util/concurrent/ConcurrentSkipListMap.java	Thu Apr 02 16:51:55 2009 -0700
+++ b/jdk/src/share/classes/java/util/concurrent/ConcurrentSkipListMap.java	Fri Apr 03 10:29:13 2009 -0700
@@ -2394,15 +2394,14 @@
                                       boolean fromInclusive,
                                       E toElement,
                                       boolean toInclusive) {
-            return new ConcurrentSkipListSet<E>
-                (m.subMap(fromElement, fromInclusive,
-                          toElement,   toInclusive));
+            return new KeySet<E>(m.subMap(fromElement, fromInclusive,
+                                          toElement,   toInclusive));
         }
         public NavigableSet<E> headSet(E toElement, boolean inclusive) {
-            return new ConcurrentSkipListSet<E>(m.headMap(toElement, inclusive));
+            return new KeySet<E>(m.headMap(toElement, inclusive));
         }
         public NavigableSet<E> tailSet(E fromElement, boolean inclusive) {
-            return new ConcurrentSkipListSet<E>(m.tailMap(fromElement, inclusive));
+            return new KeySet<E>(m.tailMap(fromElement, inclusive));
         }
         public NavigableSet<E> subSet(E fromElement, E toElement) {
             return subSet(fromElement, true, toElement, false);
@@ -2414,7 +2413,7 @@
             return tailSet(fromElement, true);
         }
         public NavigableSet<E> descendingSet() {
-            return new ConcurrentSkipListSet(m.descendingMap());
+            return new KeySet(m.descendingMap());
         }
     }
 
--- a/jdk/src/share/classes/java/util/concurrent/locks/AbstractQueuedLongSynchronizer.java	Thu Apr 02 16:51:55 2009 -0700
+++ b/jdk/src/share/classes/java/util/concurrent/locks/AbstractQueuedLongSynchronizer.java	Fri Apr 03 10:29:13 2009 -0700
@@ -166,6 +166,11 @@
         static final int SIGNAL    = -1;
         /** waitStatus value to indicate thread is waiting on condition */
         static final int CONDITION = -2;
+        /**
+         * waitStatus value to indicate the next acquireShared should
+         * unconditionally propagate
+         */
+        static final int PROPAGATE = -3;
 
         /**
          * Status field, taking on only the values:
@@ -180,10 +185,16 @@
          *               Nodes never leave this state. In particular,
          *               a thread with cancelled node never again blocks.
          *   CONDITION:  This node is currently on a condition queue.
-         *               It will not be used as a sync queue node until
-         *               transferred. (Use of this value here
-         *               has nothing to do with the other uses
-         *               of the field, but simplifies mechanics.)
+         *               It will not be used as a sync queue node
+         *               until transferred, at which time the status
+         *               will be set to 0. (Use of this value here has
+         *               nothing to do with the other uses of the
+         *               field, but simplifies mechanics.)
+         *   PROPAGATE:  A releaseShared should be propagated to other
+         *               nodes. This is set (for head node only) in
+         *               doReleaseShared to ensure propagation
+         *               continues, even if other operations have
+         *               since intervened.
          *   0:          None of the above
          *
          * The values are arranged numerically to simplify use.
@@ -403,10 +414,13 @@
      */
     private void unparkSuccessor(Node node) {
         /*
-         * Try to clear status in anticipation of signalling.  It is
-         * OK if this fails or if status is changed by waiting thread.
+         * If status is negative (i.e., possibly needing signal) try
+         * to clear in anticipation of signalling.  It is OK if this
+         * fails or if status is changed by waiting thread.
          */
-        compareAndSetWaitStatus(node, Node.SIGNAL, 0);
+        int ws = node.waitStatus;
+        if (ws < 0)
+            compareAndSetWaitStatus(node, ws, 0);
 
         /*
          * Thread to unpark is held in successor, which is normally
@@ -426,23 +440,70 @@
     }
 
     /**
+     * Release action for shared mode -- signal successor and ensure
+     * propagation. (Note: For exclusive mode, release just amounts
+     * to calling unparkSuccessor of head if it needs signal.)
+     */
+    private void doReleaseShared() {
+        /*
+         * Ensure that a release propagates, even if there are other
+         * in-progress acquires/releases.  This proceeds in the usual
+         * way of trying to unparkSuccessor of head if it needs
+         * signal. But if it does not, status is set to PROPAGATE to
+         * ensure that upon release, propagation continues.
+         * Additionally, we must loop in case a new node is added
+         * while we are doing this. Also, unlike other uses of
+         * unparkSuccessor, we need to know if CAS to reset status
+         * fails, if so rechecking.
+         */
+        for (;;) {
+            Node h = head;
+            if (h != null && h != tail) {
+                int ws = h.waitStatus;
+                if (ws == Node.SIGNAL) {
+                    if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
+                        continue;            // loop to recheck cases
+                    unparkSuccessor(h);
+                }
+                else if (ws == 0 &&
+                         !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
+                    continue;                // loop on failed CAS
+            }
+            if (h == head)                   // loop if head changed
+                break;
+        }
+    }
+
+    /**
      * Sets head of queue, and checks if successor may be waiting
-     * in shared mode, if so propagating if propagate > 0.
+     * in shared mode, if so propagating if either propagate > 0 or
+     * PROPAGATE status was set.
      *
-     * @param pred the node holding waitStatus for node
      * @param node the node
      * @param propagate the return value from a tryAcquireShared
      */
     private void setHeadAndPropagate(Node node, long propagate) {
+        Node h = head; // Record old head for check below
         setHead(node);
-        if (propagate > 0 && node.waitStatus != 0) {
-            /*
-             * Don't bother fully figuring out successor.  If it
-             * looks null, call unparkSuccessor anyway to be safe.
-             */
+        /*
+         * Try to signal next queued node if:
+         *   Propagation was indicated by caller,
+         *     or was recorded (as h.waitStatus) by a previous operation
+         *     (note: this uses sign-check of waitStatus because
+         *      PROPAGATE status may transition to SIGNAL.)
+         * and
+         *   The next node is waiting in shared mode,
+         *     or we don't know, because it appears null
+         *
+         * The conservatism in both of these checks may cause
+         * unnecessary wake-ups, but only when there are multiple
+         * racing acquires/releases, so most need signals now or soon
+         * anyway.
+         */
+        if (propagate > 0 || h == null || h.waitStatus < 0) {
             Node s = node.next;
             if (s == null || s.isShared())
-                unparkSuccessor(node);
+                doReleaseShared();
         }
     }
 
@@ -465,23 +526,27 @@
         while (pred.waitStatus > 0)
             node.prev = pred = pred.prev;
 
-        // Getting this before setting waitStatus ensures staleness
+        // predNext is the apparent node to unsplice. CASes below will
+        // fail if not, in which case, we lost race vs another cancel
+        // or signal, so no further action is necessary.
         Node predNext = pred.next;
 
-        // Can use unconditional write instead of CAS here
+        // Can use unconditional write instead of CAS here.
+        // After this atomic step, other Nodes can skip past us.
+        // Before, we are free of interference from other threads.
         node.waitStatus = Node.CANCELLED;
 
-        // If we are the tail, remove ourselves
+        // If we are the tail, remove ourselves.
         if (node == tail && compareAndSetTail(node, pred)) {
             compareAndSetNext(pred, predNext, null);
         } else {
-            // If "active" predecessor found...
-            if (pred != head
-                && (pred.waitStatus == Node.SIGNAL
-                    || compareAndSetWaitStatus(pred, 0, Node.SIGNAL))
-                && pred.thread != null) {
-
-                // If successor is active, set predecessor's next link
+            // If successor needs signal, try to set pred's next-link
+            // so it will get one. Otherwise wake it up to propagate.
+            int ws;
+            if (pred != head &&
+                ((ws = pred.waitStatus) == Node.SIGNAL ||
+                 (ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) &&
+                pred.thread != null) {
                 Node next = node.next;
                 if (next != null && next.waitStatus <= 0)
                     compareAndSetNext(pred, predNext, next);
@@ -503,14 +568,14 @@
      * @return {@code true} if thread should block
      */
     private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
-        int s = pred.waitStatus;
-        if (s < 0)
+        int ws = pred.waitStatus;
+        if (ws == Node.SIGNAL)
             /*
              * This node has already set status asking a release
              * to signal it, so it can safely park.
              */
             return true;
-        if (s > 0) {
+        if (ws > 0) {
             /*
              * Predecessor was cancelled. Skip over predecessors and
              * indicate retry.
@@ -519,14 +584,14 @@
                 node.prev = pred = pred.prev;
             } while (pred.waitStatus > 0);
             pred.next = node;
-        }
-        else
+        } else {
             /*
-             * Indicate that we need a signal, but don't park yet. Caller
-             * will need to retry to make sure it cannot acquire before
-             * parking.
+             * waitStatus must be 0 or PROPAGATE.  Indicate that we
+             * need a signal, but don't park yet.  Caller will need to
+             * retry to make sure it cannot acquire before parking.
              */
-            compareAndSetWaitStatus(pred, 0, Node.SIGNAL);
+            compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
+        }
         return false;
     }
 
@@ -1046,9 +1111,7 @@
      */
     public final boolean releaseShared(long arg) {
         if (tryReleaseShared(arg)) {
-            Node h = head;
-            if (h != null && h.waitStatus != 0)
-                unparkSuccessor(h);
+            doReleaseShared();
             return true;
         }
         return false;
@@ -1390,8 +1453,8 @@
          * case the waitStatus can be transiently and harmlessly wrong).
          */
         Node p = enq(node);
-        int c = p.waitStatus;
-        if (c > 0 || !compareAndSetWaitStatus(p, c, Node.SIGNAL))
+        int ws = p.waitStatus;
+        if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
             LockSupport.unpark(node.thread);
         return true;
     }
--- a/jdk/src/share/classes/java/util/concurrent/locks/AbstractQueuedSynchronizer.java	Thu Apr 02 16:51:55 2009 -0700
+++ b/jdk/src/share/classes/java/util/concurrent/locks/AbstractQueuedSynchronizer.java	Fri Apr 03 10:29:13 2009 -0700
@@ -389,6 +389,11 @@
         static final int SIGNAL    = -1;
         /** waitStatus value to indicate thread is waiting on condition */
         static final int CONDITION = -2;
+        /**
+         * waitStatus value to indicate the next acquireShared should
+         * unconditionally propagate
+         */
+        static final int PROPAGATE = -3;
 
         /**
          * Status field, taking on only the values:
@@ -403,10 +408,16 @@
          *               Nodes never leave this state. In particular,
          *               a thread with cancelled node never again blocks.
          *   CONDITION:  This node is currently on a condition queue.
-         *               It will not be used as a sync queue node until
-         *               transferred. (Use of this value here
-         *               has nothing to do with the other uses
-         *               of the field, but simplifies mechanics.)
+         *               It will not be used as a sync queue node
+         *               until transferred, at which time the status
+         *               will be set to 0. (Use of this value here has
+         *               nothing to do with the other uses of the
+         *               field, but simplifies mechanics.)
+         *   PROPAGATE:  A releaseShared should be propagated to other
+         *               nodes. This is set (for head node only) in
+         *               doReleaseShared to ensure propagation
+         *               continues, even if other operations have
+         *               since intervened.
          *   0:          None of the above
          *
          * The values are arranged numerically to simplify use.
@@ -626,10 +637,13 @@
      */
     private void unparkSuccessor(Node node) {
         /*
-         * Try to clear status in anticipation of signalling.  It is
-         * OK if this fails or if status is changed by waiting thread.
+         * If status is negative (i.e., possibly needing signal) try
+         * to clear in anticipation of signalling.  It is OK if this
+         * fails or if status is changed by waiting thread.
          */
-        compareAndSetWaitStatus(node, Node.SIGNAL, 0);
+        int ws = node.waitStatus;
+        if (ws < 0)
+            compareAndSetWaitStatus(node, ws, 0);
 
         /*
          * Thread to unpark is held in successor, which is normally
@@ -649,23 +663,70 @@
     }
 
     /**
+     * Release action for shared mode -- signal successor and ensure
+     * propagation. (Note: For exclusive mode, release just amounts
+     * to calling unparkSuccessor of head if it needs signal.)
+     */
+    private void doReleaseShared() {
+        /*
+         * Ensure that a release propagates, even if there are other
+         * in-progress acquires/releases.  This proceeds in the usual
+         * way of trying to unparkSuccessor of head if it needs
+         * signal. But if it does not, status is set to PROPAGATE to
+         * ensure that upon release, propagation continues.
+         * Additionally, we must loop in case a new node is added
+         * while we are doing this. Also, unlike other uses of
+         * unparkSuccessor, we need to know if CAS to reset status
+         * fails, if so rechecking.
+         */
+        for (;;) {
+            Node h = head;
+            if (h != null && h != tail) {
+                int ws = h.waitStatus;
+                if (ws == Node.SIGNAL) {
+                    if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
+                        continue;            // loop to recheck cases
+                    unparkSuccessor(h);
+                }
+                else if (ws == 0 &&
+                         !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
+                    continue;                // loop on failed CAS
+            }
+            if (h == head)                   // loop if head changed
+                break;
+        }
+    }
+
+    /**
      * Sets head of queue, and checks if successor may be waiting
-     * in shared mode, if so propagating if propagate > 0.
+     * in shared mode, if so propagating if either propagate > 0 or
+     * PROPAGATE status was set.
      *
-     * @param pred the node holding waitStatus for node
      * @param node the node
      * @param propagate the return value from a tryAcquireShared
      */
     private void setHeadAndPropagate(Node node, int propagate) {
+        Node h = head; // Record old head for check below
         setHead(node);
-        if (propagate > 0 && node.waitStatus != 0) {
-            /*
-             * Don't bother fully figuring out successor.  If it
-             * looks null, call unparkSuccessor anyway to be safe.
-             */
+        /*
+         * Try to signal next queued node if:
+         *   Propagation was indicated by caller,
+         *     or was recorded (as h.waitStatus) by a previous operation
+         *     (note: this uses sign-check of waitStatus because
+         *      PROPAGATE status may transition to SIGNAL.)
+         * and
+         *   The next node is waiting in shared mode,
+         *     or we don't know, because it appears null
+         *
+         * The conservatism in both of these checks may cause
+         * unnecessary wake-ups, but only when there are multiple
+         * racing acquires/releases, so most need signals now or soon
+         * anyway.
+         */
+        if (propagate > 0 || h == null || h.waitStatus < 0) {
             Node s = node.next;
             if (s == null || s.isShared())
-                unparkSuccessor(node);
+                doReleaseShared();
         }
     }
 
@@ -688,23 +749,27 @@
         while (pred.waitStatus > 0)
             node.prev = pred = pred.prev;
 
-        // Getting this before setting waitStatus ensures staleness
+        // predNext is the apparent node to unsplice. CASes below will
+        // fail if not, in which case, we lost race vs another cancel
+        // or signal, so no further action is necessary.
         Node predNext = pred.next;
 
-        // Can use unconditional write instead of CAS here
+        // Can use unconditional write instead of CAS here.
+        // After this atomic step, other Nodes can skip past us.
+        // Before, we are free of interference from other threads.
         node.waitStatus = Node.CANCELLED;
 
-        // If we are the tail, remove ourselves
+        // If we are the tail, remove ourselves.
         if (node == tail && compareAndSetTail(node, pred)) {
             compareAndSetNext(pred, predNext, null);
         } else {
-            // If "active" predecessor found...
-            if (pred != head
-                && (pred.waitStatus == Node.SIGNAL
-                    || compareAndSetWaitStatus(pred, 0, Node.SIGNAL))
-                && pred.thread != null) {
-
-                // If successor is active, set predecessor's next link
+            // If successor needs signal, try to set pred's next-link
+            // so it will get one. Otherwise wake it up to propagate.
+            int ws;
+            if (pred != head &&
+                ((ws = pred.waitStatus) == Node.SIGNAL ||
+                 (ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) &&
+                pred.thread != null) {
                 Node next = node.next;
                 if (next != null && next.waitStatus <= 0)
                     compareAndSetNext(pred, predNext, next);
@@ -726,14 +791,14 @@
      * @return {@code true} if thread should block
      */
     private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
-        int s = pred.waitStatus;
-        if (s < 0)
+        int ws = pred.waitStatus;
+        if (ws == Node.SIGNAL)
             /*
              * This node has already set status asking a release
              * to signal it, so it can safely park.
              */
             return true;
-        if (s > 0) {
+        if (ws > 0) {
             /*
              * Predecessor was cancelled. Skip over predecessors and
              * indicate retry.
@@ -742,14 +807,14 @@
                 node.prev = pred = pred.prev;
             } while (pred.waitStatus > 0);
             pred.next = node;
-        }
-        else
+        } else {
             /*
-             * Indicate that we need a signal, but don't park yet. Caller
-             * will need to retry to make sure it cannot acquire before
-             * parking.
+             * waitStatus must be 0 or PROPAGATE.  Indicate that we
+             * need a signal, but don't park yet.  Caller will need to
+             * retry to make sure it cannot acquire before parking.
              */
-            compareAndSetWaitStatus(pred, 0, Node.SIGNAL);
+            compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
+        }
         return false;
     }
 
@@ -1269,9 +1334,7 @@
      */
     public final boolean releaseShared(int arg) {
         if (tryReleaseShared(arg)) {
-            Node h = head;
-            if (h != null && h.waitStatus != 0)
-                unparkSuccessor(h);
+            doReleaseShared();
             return true;
         }
         return false;
@@ -1613,8 +1676,8 @@
          * case the waitStatus can be transiently and harmlessly wrong).
          */
         Node p = enq(node);
-        int c = p.waitStatus;
-        if (c > 0 || !compareAndSetWaitStatus(p, c, Node.SIGNAL))
+        int ws = p.waitStatus;
+        if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
             LockSupport.unpark(node.thread);
         return true;
     }
--- a/jdk/src/share/classes/java/util/concurrent/locks/ReentrantReadWriteLock.java	Thu Apr 02 16:51:55 2009 -0700
+++ b/jdk/src/share/classes/java/util/concurrent/locks/ReentrantReadWriteLock.java	Fri Apr 03 10:29:13 2009 -0700
@@ -276,7 +276,7 @@
          * Maintained as a ThreadLocal; cached in cachedHoldCounter
          */
         static final class HoldCounter {
-            int count;
+            int count = 0;
             // Use id, not reference, to avoid garbage retention
             final long tid = Thread.currentThread().getId();
         }
@@ -293,8 +293,9 @@
         }
 
         /**
-         * The number of read locks held by current thread.
+         * The number of reentrant read locks held by current thread.
          * Initialized only in constructor and readObject.
+         * Removed whenever a thread's read hold count drops to 0.
          */
         private transient ThreadLocalHoldCounter readHolds;
 
@@ -304,17 +305,35 @@
          * where the next thread to release is the last one to
          * acquire. This is non-volatile since it is just used
          * as a heuristic, and would be great for threads to cache.
+         *
+         * <p>Can outlive the Thread for which it is caching the read
+         * hold count, but avoids garbage retention by not retaining a
+         * reference to the Thread.
+         *
+         * <p>Accessed via a benign data race; relies on the memory
+         * model's final field and out-of-thin-air guarantees.
          */
         private transient HoldCounter cachedHoldCounter;
 
         /**
          * firstReader is the first thread to have acquired the read lock.
          * firstReaderHoldCount is firstReader's hold count.
-         * This allows tracking of read holds for uncontended read
+         *
+         * <p>More precisely, firstReader is the unique thread that last
+         * changed the shared count from 0 to 1, and has not released the
+         * read lock since then; null if there is no such thread.
+         *
+         * <p>Cannot cause garbage retention unless the thread terminated
+         * without relinquishing its read locks, since tryReleaseShared
+         * sets it to null.
+         *
+         * <p>Accessed via a benign data race; relies on the memory
+         * model's out-of-thin-air guarantees for references.
+         *
+         * <p>This allows tracking of read holds for uncontended read
          * locks to be very cheap.
          */
-        private final static long INVALID_THREAD_ID = -1;
-        private transient long firstReader = INVALID_THREAD_ID;
+        private transient Thread firstReader = null;
         private transient int firstReaderHoldCount;
 
         Sync() {
@@ -393,16 +412,16 @@
         }
 
         protected final boolean tryReleaseShared(int unused) {
-            long tid = Thread.currentThread().getId();
-            if (firstReader == tid) {
+            Thread current = Thread.currentThread();
+            if (firstReader == current) {
                 // assert firstReaderHoldCount > 0;
                 if (firstReaderHoldCount == 1)
-                    firstReader = INVALID_THREAD_ID;
+                    firstReader = null;
                 else
                     firstReaderHoldCount--;
             } else {
                 HoldCounter rh = cachedHoldCounter;
-                if (rh == null || rh.tid != tid)
+                if (rh == null || rh.tid != current.getId())
                     rh = readHolds.get();
                 int count = rh.count;
                 if (count <= 1) {
@@ -416,6 +435,9 @@
                 int c = getState();
                 int nextc = c - SHARED_UNIT;
                 if (compareAndSetState(c, nextc))
+                    // Releasing the read lock has no effect on readers,
+                    // but it may allow waiting writers to proceed if
+                    // both read and write locks are now free.
                     return nextc == 0;
             }
         }
@@ -450,15 +472,14 @@
             if (!readerShouldBlock() &&
                 r < MAX_COUNT &&
                 compareAndSetState(c, c + SHARED_UNIT)) {
-                long tid = current.getId();
                 if (r == 0) {
-                    firstReader = tid;
+                    firstReader = current;
                     firstReaderHoldCount = 1;
-                } else if (firstReader == tid) {
+                } else if (firstReader == current) {
                     firstReaderHoldCount++;
                 } else {
                     HoldCounter rh = cachedHoldCounter;
-                    if (rh == null || rh.tid != tid)
+                    if (rh == null || rh.tid != current.getId())
                         cachedHoldCounter = rh = readHolds.get();
                     else if (rh.count == 0)
                         readHolds.set(rh);
@@ -485,19 +506,17 @@
                 int c = getState();
                 if (exclusiveCount(c) != 0) {
                     if (getExclusiveOwnerThread() != current)
-                        //if (removeNeeded) readHolds.remove();
                         return -1;
                     // else we hold the exclusive lock; blocking here
                     // would cause deadlock.
                 } else if (readerShouldBlock()) {
                     // Make sure we're not acquiring read lock reentrantly
-                    long tid = current.getId();
-                    if (firstReader == tid) {
+                    if (firstReader == current) {
                         // assert firstReaderHoldCount > 0;
                     } else {
                         if (rh == null) {
                             rh = cachedHoldCounter;
-                            if (rh == null || rh.tid != tid) {
+                            if (rh == null || rh.tid != current.getId()) {
                                 rh = readHolds.get();
                                 if (rh.count == 0)
                                     readHolds.remove();
@@ -510,25 +529,20 @@
                 if (sharedCount(c) == MAX_COUNT)
                     throw new Error("Maximum lock count exceeded");
                 if (compareAndSetState(c, c + SHARED_UNIT)) {
-                    long tid = current.getId();
                     if (sharedCount(c) == 0) {
-                        firstReader = tid;
+                        firstReader = current;
                         firstReaderHoldCount = 1;
-                    } else if (firstReader == tid) {
+                    } else if (firstReader == current) {
                         firstReaderHoldCount++;
                     } else {
-                        if (rh == null) {
+                        if (rh == null)
                             rh = cachedHoldCounter;
-                            if (rh != null && rh.tid == tid) {
-                                if (rh.count == 0)
-                                    readHolds.set(rh);
-                            } else {
-                                rh = readHolds.get();
-                            }
-                        } else if (rh.count == 0)
+                        if (rh == null || rh.tid != current.getId())
+                            rh = readHolds.get();
+                        else if (rh.count == 0)
                             readHolds.set(rh);
+                        rh.count++;
                         cachedHoldCounter = rh; // cache for release
-                        rh.count++;
                     }
                     return 1;
                 }
@@ -572,15 +586,14 @@
                 if (r == MAX_COUNT)
                     throw new Error("Maximum lock count exceeded");
                 if (compareAndSetState(c, c + SHARED_UNIT)) {
-                    long tid = current.getId();
                     if (r == 0) {
-                        firstReader = tid;
+                        firstReader = current;
                         firstReaderHoldCount = 1;
-                    } else if (firstReader == tid) {
+                    } else if (firstReader == current) {
                         firstReaderHoldCount++;
                     } else {
                         HoldCounter rh = cachedHoldCounter;
-                        if (rh == null || rh.tid != tid)
+                        if (rh == null || rh.tid != current.getId())
                             cachedHoldCounter = rh = readHolds.get();
                         else if (rh.count == 0)
                             readHolds.set(rh);
@@ -626,12 +639,12 @@
             if (getReadLockCount() == 0)
                 return 0;
 
-            long tid = Thread.currentThread().getId();
-            if (firstReader == tid)
+            Thread current = Thread.currentThread();
+            if (firstReader == current)
                 return firstReaderHoldCount;
 
             HoldCounter rh = cachedHoldCounter;
-            if (rh != null && rh.tid == tid)
+            if (rh != null && rh.tid == current.getId())
                 return rh.count;
 
             int count = readHolds.get().count;
@@ -647,7 +660,6 @@
             throws java.io.IOException, ClassNotFoundException {
             s.defaultReadObject();
             readHolds = new ThreadLocalHoldCounter();
-            firstReader = INVALID_THREAD_ID;
             setState(0); // reset to unlocked state
         }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/java/util/zip/ZipConstants64.java	Fri Apr 03 10:29:13 2009 -0700
@@ -0,0 +1,77 @@
+/*
+ * Copyright 1995-1996 Sun Microsystems, Inc.  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.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package java.util.zip;
+
+/*
+ * This class defines the constants that are used by the classes
+ * which manipulate Zip64 files.
+ */
+
+class ZipConstants64 {
+
+    /*
+     * ZIP64 constants
+     */
+    static final long ZIP64_ENDSIG = 0x06064b50L;  // "PK\006\006"
+    static final long ZIP64_LOCSIG = 0x07064b50L;  // "PK\006\007"
+    static final int  ZIP64_ENDHDR = 56;           // ZIP64 end header size
+    static final int  ZIP64_LOCHDR = 20;           // ZIP64 end loc header size
+    static final int  ZIP64_EXTHDR = 24;           // EXT header size
+    static final int  ZIP64_EXTID  = 0x0001;       // Extra field Zip64 header ID
+
+    static final int  ZIP64_MAGICCOUNT = 0xFFFF;
+    static final long ZIP64_MAGICVAL = 0xFFFFFFFFL;
+
+    /*
+     * Zip64 End of central directory (END) header field offsets
+     */
+    static final int  ZIP64_ENDLEN = 4;       // size of zip64 end of central dir
+    static final int  ZIP64_ENDVEM = 12;      // version made by
+    static final int  ZIP64_ENDVER = 14;      // version needed to extract
+    static final int  ZIP64_ENDNMD = 16;      // number of this disk
+    static final int  ZIP64_ENDDSK = 20;      // disk number of start
+    static final int  ZIP64_ENDTOD = 24;      // total number of entries on this disk
+    static final int  ZIP64_ENDTOT = 32;      // total number of entries
+    static final int  ZIP64_ENDSIZ = 40;      // central directory size in bytes
+    static final int  ZIP64_ENDOFF = 48;      // offset of first CEN header
+    static final int  ZIP64_ENDEXT = 56;      // zip64 extensible data sector
+
+    /*
+     * Zip64 End of central directory locator field offsets
+     */
+    static final int  ZIP64_LOCDSK = 4;       // disk number start
+    static final int  ZIP64_LOCOFF = 8;       // offset of zip64 end
+    static final int  ZIP64_LOCTOT = 16;      // total number of disks
+
+    /*
+     * Zip64 Extra local (EXT) header field offsets
+     */
+    static final int  ZIP64_EXTCRC = 4;       // uncompressed file crc-32 value
+    static final int  ZIP64_EXTSIZ = 8;       // compressed size, 8-byte
+    static final int  ZIP64_EXTLEN = 16;      // uncompressed size, 8-byte
+
+    private ZipConstants64() {}
+}
--- a/jdk/src/share/classes/java/util/zip/ZipEntry.java	Thu Apr 02 16:51:55 2009 -0700
+++ b/jdk/src/share/classes/java/util/zip/ZipEntry.java	Fri Apr 03 10:29:13 2009 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright 1995-2005 Sun Microsystems, Inc.  All Rights Reserved.
+ * Copyright 1995-2009 Sun Microsystems, Inc.  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
@@ -144,11 +144,13 @@
      * Sets the uncompressed size of the entry data.
      * @param size the uncompressed size in bytes
      * @exception IllegalArgumentException if the specified size is less
-     *            than 0 or greater than 0xFFFFFFFF bytes
+     *            than 0, is greater than 0xFFFFFFFF when
+     *            <a href="package-summary.html#zip64">ZIP64 format</a> is not supported,
+     *            or is less than 0 when ZIP64 is supported
      * @see #getSize()
      */
     public void setSize(long size) {
-        if (size < 0 || size > 0xFFFFFFFFL) {
+        if (size < 0) {
             throw new IllegalArgumentException("invalid entry size");
         }
         this.size = size;
--- a/jdk/src/share/classes/java/util/zip/ZipInputStream.java	Thu Apr 02 16:51:55 2009 -0700
+++ b/jdk/src/share/classes/java/util/zip/ZipInputStream.java	Fri Apr 03 10:29:13 2009 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright 1996-2006 Sun Microsystems, Inc.  All Rights Reserved.
+ * Copyright 1996-2009 Sun Microsystems, Inc.  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
@@ -29,6 +29,7 @@
 import java.io.IOException;
 import java.io.EOFException;
 import java.io.PushbackInputStream;
+import static java.util.zip.ZipConstants64.*;
 
 /**
  * This class implements an input stream filter for reading files in the
@@ -285,6 +286,29 @@
             byte[] bb = new byte[len];
             readFully(bb, 0, len);
             e.setExtra(bb);
+            // extra fields are in "HeaderID(2)DataSize(2)Data... format
+            if (e.csize == ZIP64_MAGICVAL || e.size == ZIP64_MAGICVAL) {
+                int off = 0;
+                while (off + 4 < len) {
+                    int sz = get16(bb, off + 2);
+                    if (get16(bb, off) == ZIP64_EXTID) {
+                        off += 4;
+                        // LOC extra zip64 entry MUST include BOTH original and
+                        // compressed file size fields
+                        if (sz < 16 || (off + sz) > len ) {
+                            // Invalid zip64 extra fields, simply skip. Even it's
+                            // rare, it's possible the entry size happens to be
+                            // the magic value and it "accidnetly" has some bytes
+                            // in extra match the id.
+                            return e;
+                        }
+                        e.size = get64(bb, off);
+                        e.csize = get64(bb, off + 8);
+                        break;
+                    }
+                    off += (sz + 4);
+                }
+            }
         }
         return e;
     }
@@ -375,18 +399,36 @@
         }
         if ((flag & 8) == 8) {
             /* "Data Descriptor" present */
-            readFully(tmpbuf, 0, EXTHDR);
-            long sig = get32(tmpbuf, 0);
-            if (sig != EXTSIG) { // no EXTSIG present
-                e.crc = sig;
-                e.csize = get32(tmpbuf, EXTSIZ - EXTCRC);
-                e.size = get32(tmpbuf, EXTLEN - EXTCRC);
-                ((PushbackInputStream)in).unread(
-                                           tmpbuf, EXTHDR - EXTCRC - 1, EXTCRC);
+            if (inf.getBytesWritten() > ZIP64_MAGICVAL ||
+                inf.getBytesRead() > ZIP64_MAGICVAL) {
+                // ZIP64 format
+                readFully(tmpbuf, 0, ZIP64_EXTHDR);
+                long sig = get32(tmpbuf, 0);
+                if (sig != EXTSIG) { // no EXTSIG present
+                    e.crc = sig;
+                    e.csize = get64(tmpbuf, ZIP64_EXTSIZ - ZIP64_EXTCRC);
+                    e.size = get64(tmpbuf, ZIP64_EXTLEN - ZIP64_EXTCRC);
+                    ((PushbackInputStream)in).unread(
+                        tmpbuf, ZIP64_EXTHDR - ZIP64_EXTCRC - 1, ZIP64_EXTCRC);
+                } else {
+                    e.crc = get32(tmpbuf, ZIP64_EXTCRC);
+                    e.csize = get64(tmpbuf, ZIP64_EXTSIZ);
+                    e.size = get64(tmpbuf, ZIP64_EXTLEN);
+                }
             } else {
-                e.crc = get32(tmpbuf, EXTCRC);
-                e.csize = get32(tmpbuf, EXTSIZ);
-                e.size = get32(tmpbuf, EXTLEN);
+                readFully(tmpbuf, 0, EXTHDR);
+                long sig = get32(tmpbuf, 0);
+                if (sig != EXTSIG) { // no EXTSIG present
+                    e.crc = sig;
+                    e.csize = get32(tmpbuf, EXTSIZ - EXTCRC);
+                    e.size = get32(tmpbuf, EXTLEN - EXTCRC);
+                    ((PushbackInputStream)in).unread(
+                                               tmpbuf, EXTHDR - EXTCRC - 1, EXTCRC);
+                } else {
+                    e.crc = get32(tmpbuf, EXTCRC);
+                    e.csize = get32(tmpbuf, EXTSIZ);
+                    e.size = get32(tmpbuf, EXTLEN);
+                }
             }
         }
         if (e.size != inf.getBytesWritten()) {
@@ -433,6 +475,14 @@
      * The bytes are assumed to be in Intel (little-endian) byte order.
      */
     private static final long get32(byte b[], int off) {
-        return get16(b, off) | ((long)get16(b, off+2) << 16);
+        return (get16(b, off) | ((long)get16(b, off+2) << 16)) & 0xffffffffL;
+    }
+
+    /*
+     * Fetches signed 64-bit value from byte array at specified offset.
+     * The bytes are assumed to be in Intel (little-endian) byte order.
+     */
+    private static final long get64(byte b[], int off) {
+        return get32(b, off) | (get32(b, off+4) << 32);
     }
 }
--- a/jdk/src/share/classes/java/util/zip/ZipOutputStream.java	Thu Apr 02 16:51:55 2009 -0700
+++ b/jdk/src/share/classes/java/util/zip/ZipOutputStream.java	Fri Apr 03 10:29:13 2009 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright 1996-2008 Sun Microsystems, Inc.  All Rights Reserved.
+ * Copyright 1996-2009 Sun Microsystems, Inc.  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
@@ -29,6 +29,7 @@
 import java.io.IOException;
 import java.util.Vector;
 import java.util.HashSet;
+import static java.util.zip.ZipConstants64.*;
 
 /**
  * This class implements an output stream filter for writing files in the
@@ -343,26 +344,52 @@
     private void writeLOC(XEntry xentry) throws IOException {
         ZipEntry e = xentry.entry;
         int flag = xentry.flag;
+        int elen = (e.extra != null) ? e.extra.length : 0;
+        boolean hasZip64 = false;
+
         writeInt(LOCSIG);           // LOC header signature
-        writeShort(version(e));     // version needed to extract
-        writeShort(flag);           // general purpose bit flag
-        writeShort(e.method);       // compression method
-        writeInt(e.time);           // last modification time
+
         if ((flag & 8) == 8) {
+            writeShort(version(e));     // version needed to extract
+            writeShort(flag);           // general purpose bit flag
+            writeShort(e.method);       // compression method
+            writeInt(e.time);           // last modification time
+
             // store size, uncompressed size, and crc-32 in data descriptor
             // immediately following compressed entry data
             writeInt(0);
             writeInt(0);
             writeInt(0);
         } else {
-            writeInt(e.crc);        // crc-32
-            writeInt(e.csize);      // compressed size
-            writeInt(e.size);       // uncompressed size
+            if (e.csize >= ZIP64_MAGICVAL || e.size >= ZIP64_MAGICVAL) {
+                hasZip64 = true;
+                writeShort(45);         // ver 4.5 for zip64
+            } else {
+                writeShort(version(e)); // version needed to extract
+            }
+            writeShort(flag);           // general purpose bit flag
+            writeShort(e.method);       // compression method
+            writeInt(e.time);           // last modification time
+            writeInt(e.crc);            // crc-32
+            if (hasZip64) {
+                writeInt(ZIP64_MAGICVAL);
+                writeInt(ZIP64_MAGICVAL);
+                elen += 20;        //headid(2) + size(2) + size(8) + csize(8)
+            } else {
+                writeInt(e.csize);  // compressed size
+                writeInt(e.size);   // uncompressed size
+            }
         }
         byte[] nameBytes = getUTF8Bytes(e.name);
         writeShort(nameBytes.length);
-        writeShort(e.extra != null ? e.extra.length : 0);
+        writeShort(elen);
         writeBytes(nameBytes, 0, nameBytes.length);
+        if (hasZip64) {
+            writeShort(ZIP64_EXTID);
+            writeShort(16);
+            writeLong(e.size);
+            writeLong(e.csize);
+        }
         if (e.extra != null) {
             writeBytes(e.extra, 0, e.extra.length);
         }
@@ -375,8 +402,13 @@
     private void writeEXT(ZipEntry e) throws IOException {
         writeInt(EXTSIG);           // EXT header signature
         writeInt(e.crc);            // crc-32
-        writeInt(e.csize);          // compressed size
-        writeInt(e.size);           // uncompressed size
+        if (e.csize >= ZIP64_MAGICVAL || e.size >= ZIP64_MAGICVAL) {
+            writeLong(e.csize);
+            writeLong(e.size);
+        } else {
+            writeInt(e.csize);          // compressed size
+            writeInt(e.size);           // uncompressed size
+        }
     }
 
     /*
@@ -387,18 +419,49 @@
         ZipEntry e  = xentry.entry;
         int flag = xentry.flag;
         int version = version(e);
+
+        long csize = e.csize;
+        long size = e.size;
+        long offset = xentry.offset;
+        int e64len = 0;
+        boolean hasZip64 = false;
+        if (e.csize >= ZIP64_MAGICVAL) {
+            csize = ZIP64_MAGICVAL;
+            e64len += 8;              // csize(8)
+            hasZip64 = true;
+        }
+        if (e.size >= ZIP64_MAGICVAL) {
+            size = ZIP64_MAGICVAL;    // size(8)
+            e64len += 8;
+            hasZip64 = true;
+        }
+        if (xentry.offset >= ZIP64_MAGICVAL) {
+            offset = ZIP64_MAGICVAL;
+            e64len += 8;              // offset(8)
+            hasZip64 = true;
+        }
         writeInt(CENSIG);           // CEN header signature
-        writeShort(version);        // version made by
-        writeShort(version);        // version needed to extract
+        if (hasZip64) {
+            writeShort(45);         // ver 4.5 for zip64
+            writeShort(45);
+        } else {
+            writeShort(version);    // version made by
+            writeShort(version);    // version needed to extract
+        }
         writeShort(flag);           // general purpose bit flag
         writeShort(e.method);       // compression method
         writeInt(e.time);           // last modification time
         writeInt(e.crc);            // crc-32
-        writeInt(e.csize);          // compressed size
-        writeInt(e.size);           // uncompressed size
+        writeInt(csize);            // compressed size
+        writeInt(size);             // uncompressed size
         byte[] nameBytes = getUTF8Bytes(e.name);
         writeShort(nameBytes.length);
-        writeShort(e.extra != null ? e.extra.length : 0);
+        if (hasZip64) {
+            // + headid(2) + datasize(2)
+            writeShort(e64len + 4 + (e.extra != null ? e.extra.length : 0));
+        } else {
+            writeShort(e.extra != null ? e.extra.length : 0);
+        }
         byte[] commentBytes;
         if (e.comment != null) {
             commentBytes = getUTF8Bytes(e.comment);
@@ -410,8 +473,18 @@
         writeShort(0);              // starting disk number
         writeShort(0);              // internal file attributes (unused)
         writeInt(0);                // external file attributes (unused)
-        writeInt(xentry.offset);    // relative offset of local header
+        writeInt(offset);           // relative offset of local header
         writeBytes(nameBytes, 0, nameBytes.length);
+        if (hasZip64) {
+            writeShort(ZIP64_EXTID);// Zip64 extra
+            writeShort(e64len);
+            if (size == ZIP64_MAGICVAL)
+                writeLong(e.size);
+            if (csize == ZIP64_MAGICVAL)
+                writeLong(e.csize);
+            if (offset == ZIP64_MAGICVAL)
+                writeLong(xentry.offset);
+        }
         if (e.extra != null) {
             writeBytes(e.extra, 0, e.extra.length);
         }
@@ -424,15 +497,50 @@
      * Writes end of central directory (END) header.
      */
     private void writeEND(long off, long len) throws IOException {
+        boolean hasZip64 = false;
+        long xlen = len;
+        long xoff = off;
+        if (xlen >= ZIP64_MAGICVAL) {
+            xlen = ZIP64_MAGICVAL;
+            hasZip64 = true;
+        }
+        if (xoff >= ZIP64_MAGICVAL) {
+            xoff = ZIP64_MAGICVAL;
+            hasZip64 = true;
+        }
         int count = xentries.size();
-        writeInt(ENDSIG);           // END record signature
-        writeShort(0);              // number of this disk
-        writeShort(0);              // central directory start disk
-        writeShort(count);          // number of directory entries on disk
-        writeShort(count);          // total number of directory entries
-        writeInt(len);              // length of central directory
-        writeInt(off);              // offset of central directory
-        if (comment != null) {      // zip file comment
+        if (count >= ZIP64_MAGICCOUNT) {
+            count = ZIP64_MAGICCOUNT;
+            hasZip64 = true;
+        }
+        if (hasZip64) {
+            long off64 = written;
+            //zip64 end of central directory record
+            writeInt(ZIP64_ENDSIG);        // zip64 END record signature
+            writeLong(ZIP64_ENDHDR - 12);  // size of zip64 end
+            writeShort(45);                // version made by
+            writeShort(45);                // version needed to extract
+            writeInt(0);                   // number of this disk
+            writeInt(0);                   // central directory start disk
+            writeLong(xentries.size());    // number of directory entires on disk
+            writeLong(xentries.size());    // number of directory entires
+            writeLong(len);                // length of central directory
+            writeLong(off);                // offset of central directory
+
+            //zip64 end of central directory locator
+            writeInt(ZIP64_LOCSIG);        // zip64 END locator signature
+            writeInt(0);                   // zip64 END start disk
+            writeLong(off64);              // offset of zip64 END
+            writeInt(1);                   // total number of disks (?)
+        }
+        writeInt(ENDSIG);                 // END record signature
+        writeShort(0);                    // number of this disk
+        writeShort(0);                    // central directory start disk
+        writeShort(count);                // number of directory entries on disk
+        writeShort(count);                // total number of directory entries
+        writeInt(xlen);                   // length of central directory
+        writeInt(xoff);                   // offset of central directory
+        if (comment != null) {            // zip file comment
             byte[] b = getUTF8Bytes(comment);
             writeShort(b.length);
             writeBytes(b, 0, b.length);
@@ -464,6 +572,22 @@
     }
 
     /*
+     * Writes a 64-bit int to the output stream in little-endian byte order.
+     */
+    private void writeLong(long v) throws IOException {
+        OutputStream out = this.out;
+        out.write((int)((v >>>  0) & 0xff));
+        out.write((int)((v >>>  8) & 0xff));
+        out.write((int)((v >>> 16) & 0xff));
+        out.write((int)((v >>> 24) & 0xff));
+        out.write((int)((v >>> 32) & 0xff));
+        out.write((int)((v >>> 40) & 0xff));
+        out.write((int)((v >>> 48) & 0xff));
+        out.write((int)((v >>> 56) & 0xff));
+        written += 8;
+    }
+
+    /*
      * Writes an array of bytes to the output stream.
      */
     private void writeBytes(byte[] b, int off, int len) throws IOException {
--- a/jdk/src/share/classes/java/util/zip/package.html	Thu Apr 02 16:51:55 2009 -0700
+++ b/jdk/src/share/classes/java/util/zip/package.html	Fri Apr 03 10:29:13 2009 -0700
@@ -46,6 +46,13 @@
       </a> - a detailed description of the Info-ZIP format upon which
       the <code>java.util.zip</code> classes are based.
 <p>
+  <a name="zip64">
+  <li>An implementation may optionally support the ZIP64(tm) format extensions
+      defined by the 
+      <a href="http://www.pkware.com/documents/casestudies/APPNOTE.TXT">
+      PKWARE ZIP File Format Specification</a>. The ZIP64(tm) format extensions
+      are used to overcome the size limitations of the original ZIP format.
+<p>
   <li><a href="http://www.isi.edu/in-notes/rfc1950.txt">
       ZLIB Compressed Data Format Specification version 3.3</a>
       &nbsp;
@@ -70,7 +77,6 @@
   <li>CRC-32 checksum is described in RFC 1952 (above)
 <p>
   <li>Adler-32 checksum is described in RFC 1950 (above)
-
 </ul>
 
 
--- a/jdk/src/share/classes/sun/net/spi/DefaultProxySelector.java	Thu Apr 02 16:51:55 2009 -0700
+++ b/jdk/src/share/classes/sun/net/spi/DefaultProxySelector.java	Fri Apr 03 10:29:13 2009 -0700
@@ -78,7 +78,6 @@
     };
 
     private static boolean hasSystemProxies = false;
-    private static Properties defprops = new Properties();
 
     static {
         final String key = "java.net.useSystemProxies";
@@ -107,6 +106,9 @@
         RegexpPool hostsPool;
         String property;
 
+        static NonProxyInfo ftpNonProxyInfo = new NonProxyInfo("ftp.nonProxyHosts", null, null);
+        static NonProxyInfo httpNonProxyInfo = new NonProxyInfo("http.nonProxyHosts", null, null);
+
         NonProxyInfo(String p, String s, RegexpPool pool) {
             property = p;
             hostsSource = s;
@@ -114,8 +116,6 @@
         }
     }
 
-    private static NonProxyInfo ftpNonProxyInfo = new NonProxyInfo("ftp.nonProxyHosts", null, null);
-    private static NonProxyInfo httpNonProxyInfo = new NonProxyInfo("http.nonProxyHosts", null, null);
 
     /**
      * select() method. Where all the hard work is done.
@@ -175,13 +175,13 @@
         NonProxyInfo pinfo = null;
 
         if ("http".equalsIgnoreCase(protocol)) {
-            pinfo = httpNonProxyInfo;
+            pinfo = NonProxyInfo.httpNonProxyInfo;
         } else if ("https".equalsIgnoreCase(protocol)) {
             // HTTPS uses the same property as HTTP, for backward
             // compatibility
-            pinfo = httpNonProxyInfo;
+            pinfo = NonProxyInfo.httpNonProxyInfo;
         } else if ("ftp".equalsIgnoreCase(protocol)) {
-            pinfo = ftpNonProxyInfo;
+            pinfo = NonProxyInfo.ftpNonProxyInfo;
         }
 
         /**
@@ -334,7 +334,6 @@
         }
     }
 
-    private static final Pattern p6 = Pattern.compile("::1|(0:){7}1|(0:){1,6}:1");
     private boolean isLoopback(String host) {
         if (host == null || host.length() == 0)
             return false;
@@ -364,6 +363,7 @@
         }
 
         if (host.endsWith(":1")) {
+            final Pattern p6 = Pattern.compile("::1|(0:){7}1|(0:){1,6}:1");
             return p6.matcher(host).matches();
         }
         return false;
--- a/jdk/src/share/classes/sun/nio/ch/DatagramChannelImpl.java	Thu Apr 02 16:51:55 2009 -0700
+++ b/jdk/src/share/classes/sun/nio/ch/DatagramChannelImpl.java	Fri Apr 03 10:29:13 2009 -0700
@@ -313,11 +313,9 @@
             throw new NullPointerException();
         synchronized (readLock) {
             ensureOpen();
-            // If socket is not bound then behave as if nothing received
-            // Will be fixed by 6621699
-            if (localAddress() == null) {
-                return null;
-            }
+            // Socket was not bound before attempting receive
+            if (localAddress() == null)
+                bind(null);
             int n = 0;
             ByteBuffer bb = null;
             try {
--- a/jdk/src/share/classes/sun/nio/ch/SelChImpl.java	Thu Apr 02 16:51:55 2009 -0700
+++ b/jdk/src/share/classes/sun/nio/ch/SelChImpl.java	Fri Apr 03 10:29:13 2009 -0700
@@ -25,6 +25,7 @@
 
 package sun.nio.ch;
 
+import java.nio.channels.Channel;
 import java.io.FileDescriptor;
 import java.io.IOException;
 
@@ -35,7 +36,7 @@
  * @since 1.4
  */
 
-interface SelChImpl {
+interface SelChImpl extends Channel {
 
     FileDescriptor getFD();
 
--- a/jdk/src/share/classes/sun/security/tools/JarSigner.java	Thu Apr 02 16:51:55 2009 -0700
+++ b/jdk/src/share/classes/sun/security/tools/JarSigner.java	Fri Apr 03 10:29:13 2009 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright 1997-2007 Sun Microsystems, Inc.  All Rights Reserved.
+ * Copyright 1997-2009 Sun Microsystems, Inc.  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
@@ -32,28 +32,44 @@
 import java.math.BigInteger;
 import java.net.URI;
 import java.net.URISyntaxException;
-import java.net.URL;
-import java.net.URLClassLoader;
-import java.net.SocketTimeoutException;
 import java.text.Collator;
 import java.text.MessageFormat;
 import java.security.cert.Certificate;
 import java.security.cert.X509Certificate;
 import java.security.cert.CertificateException;
-import java.security.cert.CertificateExpiredException;
-import java.security.cert.CertificateNotYetValidException;
 import java.security.*;
 import java.lang.reflect.Constructor;
 
 import com.sun.jarsigner.ContentSigner;
 import com.sun.jarsigner.ContentSignerParameters;
+import java.net.SocketTimeoutException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.security.cert.CertPath;
+import java.security.cert.CertPathValidator;
+import java.security.cert.CertificateExpiredException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.CertificateNotYetValidException;
+import java.security.cert.PKIXParameters;
+import java.security.cert.TrustAnchor;
+import java.util.Map.Entry;
 import sun.security.x509.*;
 import sun.security.util.*;
 import sun.misc.BASE64Encoder;
 
+
 /**
  * <p>The jarsigner utility.
  *
+ * The exit codes for the main method are:
+ *
+ * 0: success
+ * 1: any error that the jar cannot be signed or verified, including:
+ *      keystore loading error
+ *      TSP communciation error
+ *      jarsigner command line error...
+ * otherwise: error codes from -strict
+ *
  * @author Roland Schemers
  * @author Jan Luehe
  */
@@ -84,8 +100,6 @@
 
     // Attention:
     // This is the entry that get launched by the security tool jarsigner.
-    // It's marked as exported private per AppServer Team's request.
-    // See http://ccc.sfbay/6428446
     public static void main(String args[]) throws Exception {
         JarSigner js = new JarSigner();
         js.run(args);
@@ -93,31 +107,32 @@
 
     static final String VERSION = "1.0";
 
-    static final int IN_KEYSTORE = 0x01;
+    static final int IN_KEYSTORE = 0x01;        // signer is in keystore
     static final int IN_SCOPE = 0x02;
-
-    // signer's certificate chain (when composing)
-    X509Certificate[] certChain;
+    static final int NOT_ALIAS = 0x04;          // alias list is NOT empty and
+                                                // signer is not in alias list
+    static final int SIGNED_BY_ALIAS = 0x08;    // signer is in alias list
 
-    /*
-     * private key
-     */
-    PrivateKey privateKey;
-    KeyStore store;
+    X509Certificate[] certChain;    // signer's cert chain (when composing)
+    PrivateKey privateKey;          // private key
+    KeyStore store;                 // the keystore specified by -keystore
+                                    // or the default keystore, never null
 
     IdentityScope scope;
 
     String keystore; // key store file
     boolean nullStream = false; // null keystore input stream (NONE)
     boolean token = false; // token-based keystore
-    String jarfile;  // jar file to sign
+    String jarfile;  // jar file to sign or verify
     String alias;    // alias to sign jar with
+    List<String> ckaliases = new ArrayList<String>(); // aliases in -verify
     char[] storepass; // keystore password
     boolean protectedPath; // protected authentication path
     String storetype; // keystore type
     String providerName; // provider name
     Vector<String> providers = null; // list of providers
-    HashMap<String,String> providerArgs = new HashMap<String, String>(); // arguments for provider constructors
+    // arguments for provider constructors
+    HashMap<String,String> providerArgs = new HashMap<String, String>();
     char[] keypass; // private key password
     String sigfile; // name of .SF file
     String sigalg; // name of signature algorithm
@@ -125,12 +140,14 @@
     String signedjar; // output filename
     String tsaUrl; // location of the Timestamping Authority
     String tsaAlias; // alias for the Timestamping Authority's certificate
+    String altCertChain; // file to read alternative cert chain from
     boolean verify = false; // verify the jar
-    boolean verbose = false; // verbose output when signing/verifying
+    String verbose = null; // verbose output when signing/verifying
     boolean showcerts = false; // show certs when verifying
     boolean debug = false; // debug
     boolean signManifest = true; // "sign" the whole manifest
     boolean externalSF = true; // leave the .SF out of the PKCS7 block
+    boolean strict = false;  // treat warnings as error
 
     // read zip entry raw bytes
     private ByteArrayOutputStream baos = new ByteArrayOutputStream(2048);
@@ -139,14 +156,22 @@
     private String altSignerClass = null;
     private String altSignerClasspath = null;
     private ZipFile zipFile = null;
+
     private boolean hasExpiredCert = false;
     private boolean hasExpiringCert = false;
     private boolean notYetValidCert = false;
-
+    private boolean chainNotValidated = false;
+    private boolean notSignedByAlias = false;
+    private boolean aliasNotInStore = false;
+    private boolean hasUnsignedEntry = false;
     private boolean badKeyUsage = false;
     private boolean badExtendedKeyUsage = false;
     private boolean badNetscapeCertType = false;
 
+    CertificateFactory certificateFactory;
+    CertPathValidator validator;
+    PKIXParameters pkixParameters;
+
     public void run(String args[]) {
         try {
             parseArgs(args);
@@ -184,14 +209,6 @@
                 }
             }
 
-            hasExpiredCert = false;
-            hasExpiringCert = false;
-            notYetValidCert = false;
-
-            badKeyUsage = false;
-            badExtendedKeyUsage = false;
-            badNetscapeCertType = false;
-
             if (verify) {
                 try {
                     loadKeyStore(keystore, false);
@@ -238,6 +255,29 @@
                 storepass = null;
             }
         }
+
+        if (strict) {
+            int exitCode = 0;
+            if (hasExpiringCert) {
+                exitCode |= 2;
+            }
+            if (chainNotValidated) {
+                // hasExpiredCert and notYetValidCert included in this case
+                exitCode |= 4;
+            }
+            if (badKeyUsage || badExtendedKeyUsage || badNetscapeCertType) {
+                exitCode |= 8;
+            }
+            if (hasUnsignedEntry) {
+                exitCode |= 16;
+            }
+            if (notSignedByAlias || aliasNotInStore) {
+                exitCode |= 32;
+            }
+            if (exitCode != 0) {
+                System.exit(exitCode);
+            }
+        }
     }
 
     /*
@@ -247,25 +287,26 @@
         /* parse flags */
         int n = 0;
 
-        for (n=0; (n < args.length) && args[n].startsWith("-"); n++) {
+        if (args.length == 0) fullusage();
+        for (n=0; n < args.length; n++) {
 
             String flags = args[n];
 
             if (collator.compare(flags, "-keystore") == 0) {
-                if (++n == args.length) usage();
+                if (++n == args.length) usageNoArg();
                 keystore = args[n];
             } else if (collator.compare(flags, "-storepass") ==0) {
-                if (++n == args.length) usage();
+                if (++n == args.length) usageNoArg();
                 storepass = args[n].toCharArray();
             } else if (collator.compare(flags, "-storetype") ==0) {
-                if (++n == args.length) usage();
+                if (++n == args.length) usageNoArg();
                 storetype = args[n];
             } else if (collator.compare(flags, "-providerName") ==0) {
-                if (++n == args.length) usage();
+                if (++n == args.length) usageNoArg();
                 providerName = args[n];
             } else if ((collator.compare(flags, "-provider") == 0) ||
                         (collator.compare(flags, "-providerClass") == 0)) {
-                if (++n == args.length) usage();
+                if (++n == args.length) usageNoArg();
                 if (providers == null) {
                     providers = new Vector<String>(3);
                 }
@@ -274,35 +315,38 @@
                 if (args.length > (n+1)) {
                     flags = args[n+1];
                     if (collator.compare(flags, "-providerArg") == 0) {
-                        if (args.length == (n+2)) usage();
+                        if (args.length == (n+2)) usageNoArg();
                         providerArgs.put(args[n], args[n+2]);
                         n += 2;
                     }
                 }
             } else if (collator.compare(flags, "-protected") ==0) {
                 protectedPath = true;
+            } else if (collator.compare(flags, "-certchain") ==0) {
+                if (++n == args.length) usageNoArg();
+                altCertChain = args[n];
             } else if (collator.compare(flags, "-debug") ==0) {
                 debug = true;
             } else if (collator.compare(flags, "-keypass") ==0) {
-                if (++n == args.length) usage();
+                if (++n == args.length) usageNoArg();
                 keypass = args[n].toCharArray();
             } else if (collator.compare(flags, "-sigfile") ==0) {
-                if (++n == args.length) usage();
+                if (++n == args.length) usageNoArg();
                 sigfile = args[n];
             } else if (collator.compare(flags, "-signedjar") ==0) {
-                if (++n == args.length) usage();
+                if (++n == args.length) usageNoArg();
                 signedjar = args[n];
             } else if (collator.compare(flags, "-tsa") ==0) {
-                if (++n == args.length) usage();
+                if (++n == args.length) usageNoArg();
                 tsaUrl = args[n];
             } else if (collator.compare(flags, "-tsacert") ==0) {
-                if (++n == args.length) usage();
+                if (++n == args.length) usageNoArg();
                 tsaAlias = args[n];
             } else if (collator.compare(flags, "-altsigner") ==0) {
-                if (++n == args.length) usage();
+                if (++n == args.length) usageNoArg();
                 altSignerClass = args[n];
             } else if (collator.compare(flags, "-altsignerpath") ==0) {
-                if (++n == args.length) usage();
+                if (++n == args.length) usageNoArg();
                 altSignerClasspath = args[n];
             } else if (collator.compare(flags, "-sectionsonly") ==0) {
                 signManifest = false;
@@ -311,30 +355,56 @@
             } else if (collator.compare(flags, "-verify") ==0) {
                 verify = true;
             } else if (collator.compare(flags, "-verbose") ==0) {
-                verbose = true;
+                verbose = "all";
+            } else if (collator.compare(flags, "-verbose:all") ==0) {
+                verbose = "all";
+            } else if (collator.compare(flags, "-verbose:summary") ==0) {
+                verbose = "summary";
+            } else if (collator.compare(flags, "-verbose:grouped") ==0) {
+                verbose = "grouped";
             } else if (collator.compare(flags, "-sigalg") ==0) {
-                if (++n == args.length) usage();
+                if (++n == args.length) usageNoArg();
                 sigalg = args[n];
             } else if (collator.compare(flags, "-digestalg") ==0) {
-                if (++n == args.length) usage();
+                if (++n == args.length) usageNoArg();
                 digestalg = args[n];
             } else if (collator.compare(flags, "-certs") ==0) {
                 showcerts = true;
+            } else if (collator.compare(flags, "-strict") ==0) {
+                strict = true;
             } else if (collator.compare(flags, "-h") == 0 ||
                         collator.compare(flags, "-help") == 0) {
-                usage();
+                fullusage();
             } else {
-                System.err.println(rb.getString("Illegal option: ") + flags);
-                usage();
+                if (!flags.startsWith("-")) {
+                    if (jarfile == null) {
+                        jarfile = flags;
+                    } else {
+                        alias = flags;
+                        ckaliases.add(alias);
+                    }
+                } else {
+                    System.err.println(
+                            rb.getString("Illegal option: ") + flags);
+                    usage();
+                }
             }
         }
 
-        if (n == args.length) usage();
-        jarfile = args[n++];
+        // -certs must always be specified with -verbose
+        if (verbose == null) showcerts = false;
 
-        if (!verify) {
-            if (n == args.length) usage();
-            alias = args[n++];
+        if (jarfile == null) {
+            System.err.println(rb.getString("Please specify jarfile name"));
+            usage();
+        }
+        if (!verify && alias == null) {
+            System.err.println(rb.getString("Please specify alias name"));
+            usage();
+        }
+        if (!verify && ckaliases.size() > 1) {
+            System.err.println(rb.getString("Only one alias can be specified"));
+            usage();
         }
 
         if (storetype == null) {
@@ -357,7 +427,6 @@
         if (token && !nullStream) {
             System.err.println(MessageFormat.format(rb.getString
                 ("-keystore must be NONE if -storetype is {0}"), storetype));
-            System.err.println();
             usage();
         }
 
@@ -365,7 +434,6 @@
             System.err.println(MessageFormat.format(rb.getString
                 ("-keypass can not be specified " +
                 "if -storetype is {0}"), storetype));
-            System.err.println();
             usage();
         }
 
@@ -374,7 +442,6 @@
                 System.err.println(rb.getString
                         ("If -protected is specified, " +
                         "then -storepass and -keypass must not be specified"));
-                System.err.println();
                 usage();
             }
         }
@@ -383,17 +450,27 @@
                 System.err.println(rb.getString
                         ("If keystore is not password protected, " +
                         "then -storepass and -keypass must not be specified"));
-                System.err.println();
                 usage();
             }
         }
     }
 
+    void usageNoArg() {
+        System.out.println(rb.getString("Option lacks argument"));
+        usage();
+    }
+
     void usage() {
+        System.out.println();
+        System.out.println(rb.getString("Please type jarsigner -help for usage"));
+        System.exit(1);
+    }
+
+    void fullusage() {
         System.out.println(rb.getString
                 ("Usage: jarsigner [options] jar-file alias"));
         System.out.println(rb.getString
-                ("       jarsigner -verify [options] jar-file"));
+                ("       jarsigner -verify [options] jar-file [alias...]"));
         System.out.println();
         System.out.println(rb.getString
                 ("[-keystore <url>]           keystore location"));
@@ -408,6 +485,9 @@
                 ("[-keypass <password>]       password for private key (if different)"));
         System.out.println();
         System.out.println(rb.getString
+                ("[-certchain <file>]         name of alternative certchain file"));
+        System.out.println();
+        System.out.println(rb.getString
                 ("[-sigfile <file>]           name of .SF/.DSA file"));
         System.out.println();
         System.out.println(rb.getString
@@ -423,7 +503,9 @@
                 ("[-verify]                   verify a signed JAR file"));
         System.out.println();
         System.out.println(rb.getString
-                ("[-verbose]                  verbose output when signing/verifying"));
+                ("[-verbose[:suboptions]]     verbose output when signing/verifying."));
+        System.out.println(rb.getString
+                ("                            suboptions can be all, grouped or summary"));
         System.out.println();
         System.out.println(rb.getString
                 ("[-certs]                    display certificates when verbose and verifying"));
@@ -457,15 +539,17 @@
         System.out.println(rb.getString
                 ("  [-providerArg <arg>]] ... master class file and constructor argument"));
         System.out.println();
+        System.out.println(rb.getString
+                ("[-strict]                   treat warnings as errors"));
+        System.out.println();
 
-        System.exit(1);
+        System.exit(0);
     }
 
     void verifyJar(String jarName)
         throws Exception
     {
-        boolean anySigned = false;
-        boolean hasUnsignedEntry = false;
+        boolean anySigned = false;  // if there exists entry inside jar signed
         JarFile jf = null;
 
         try {
@@ -494,11 +578,18 @@
 
             Manifest man = jf.getManifest();
 
+            // The map to record display info, only used when -verbose provided
+            //      key: signer info string
+            //      value: the list of files with common key
+            Map<String,List<String>> output =
+                    new LinkedHashMap<String,List<String>>();
+
             if (man != null) {
-                if (verbose) System.out.println();
+                if (verbose != null) System.out.println();
                 Enumeration<JarEntry> e = entriesVec.elements();
 
                 long now = System.currentTimeMillis();
+                String tab = rb.getString("      ");
 
                 while (e.hasMoreElements()) {
                     JarEntry je = e.nextElement();
@@ -509,77 +600,118 @@
                     hasUnsignedEntry |= !je.isDirectory() && !isSigned
                                         && !signatureRelated(name);
 
-                    if (verbose) {
-                        int inStoreOrScope = inKeyStore(signers);
-                        boolean inStore = (inStoreOrScope & IN_KEYSTORE) != 0;
-                        boolean inScope = (inStoreOrScope & IN_SCOPE) != 0;
+                    int inStoreOrScope = inKeyStore(signers);
+
+                    boolean inStore = (inStoreOrScope & IN_KEYSTORE) != 0;
+                    boolean inScope = (inStoreOrScope & IN_SCOPE) != 0;
+
+                    notSignedByAlias |= (inStoreOrScope & NOT_ALIAS) != 0;
+                    aliasNotInStore |= isSigned && (!inStore && !inScope);
+
+                    // Only used when -verbose provided
+                    StringBuffer sb = null;
+                    if (verbose != null) {
+                        sb = new StringBuffer();
                         boolean inManifest =
                             ((man.getAttributes(name) != null) ||
                              (man.getAttributes("./"+name) != null) ||
                              (man.getAttributes("/"+name) != null));
-                        System.out.print(
+                        sb.append(
                           (isSigned ? rb.getString("s") : rb.getString(" ")) +
                           (inManifest ? rb.getString("m") : rb.getString(" ")) +
                           (inStore ? rb.getString("k") : rb.getString(" ")) +
                           (inScope ? rb.getString("i") : rb.getString(" ")) +
-                          rb.getString("  "));
-                        StringBuffer sb = new StringBuffer();
-                        String s = Long.toString(je.getSize());
-                        for (int i = 6 - s.length(); i > 0; --i) {
-                            sb.append(' ');
-                        }
-                        sb.append(s).append(' ').
-                                    append(new Date(je.getTime()).toString());
-                        sb.append(' ').append(je.getName());
-                        System.out.println(sb.toString());
+                          ((inStoreOrScope & NOT_ALIAS) != 0 ?"X":" ") +
+                          rb.getString(" "));
+                        sb.append("|");
+                    }
 
-                        if (signers != null && showcerts) {
-                            String tab = rb.getString("      ");
-                            for (int i = 0; i < signers.length; i++) {
-                                System.out.println();
-                                List<? extends Certificate> certs =
-                                    signers[i].getSignerCertPath()
-                                        .getCertificates();
-                                // display the signature timestamp, if present
-                                Timestamp timestamp = signers[i].getTimestamp();
-                                if (timestamp != null) {
-                                    System.out.println(
-                                        printTimestamp(tab, timestamp));
-                                }
-                                // display the certificate(s)
-                                for (Certificate c : certs) {
-                                    System.out.println(
-                                        printCert(tab, c, true, now));
-                                }
+                    // When -certs provided, display info has extra empty
+                    // lines at the beginning and end.
+                    if (isSigned) {
+                        if (showcerts) sb.append('\n');
+                        for (CodeSigner signer: signers) {
+                            // signerInfo() must be called even if -verbose
+                            // not provided. The method updates various
+                            // warning flags.
+                            String si = signerInfo(signer, tab, now);
+                            if (showcerts) {
+                                sb.append(si);
+                                sb.append('\n');
                             }
-                            System.out.println();
                         }
-
-                    }
-                    if (isSigned) {
-                        for (int i = 0; i < signers.length; i++) {
-                            Certificate cert =
-                                signers[i].getSignerCertPath()
-                                    .getCertificates().get(0);
-                            if (cert instanceof X509Certificate) {
-                                checkCertUsage((X509Certificate)cert, null);
-                                if (!showcerts) {
-                                    long notAfter = ((X509Certificate)cert)
-                                        .getNotAfter().getTime();
-
-                                    if (notAfter < now) {
-                                        hasExpiredCert = true;
-                                    } else if (notAfter < now + SIX_MONTHS) {
-                                        hasExpiringCert = true;
-                                    }
-                                }
-                            }
+                    } else if (showcerts && !verbose.equals("all")) {
+                        // Print no info for unsigned entries when -verbose:all,
+                        // to be consistent with old behavior.
+                        if (signatureRelated(name)) {
+                            sb.append("\n" + tab + rb.getString(
+                                    "(Signature related entries)") + "\n\n");
+                        } else {
+                            sb.append("\n" + tab + rb.getString(
+                                    "(Unsigned entries)") + "\n\n");
                         }
                     }
 
+                    if (verbose != null) {
+                        String label = sb.toString();
+                        if (signatureRelated(name)) {
+                            // Entries inside META-INF and other unsigned
+                            // entries are grouped separately.
+                            label = "-" + label.substring(1);
+                        }
+
+                        // The label finally contains 2 parts separated by '|':
+                        // The legend displayed before the entry names, and
+                        // the cert info (if -certs specfied).
+
+                        if (!output.containsKey(label)) {
+                            output.put(label, new ArrayList<String>());
+                        }
+
+                        StringBuffer fb = new StringBuffer();
+                        String s = Long.toString(je.getSize());
+                        for (int i = 6 - s.length(); i > 0; --i) {
+                            fb.append(' ');
+                        }
+                        fb.append(s).append(' ').
+                                append(new Date(je.getTime()).toString());
+                        fb.append(' ').append(name);
+
+                        output.get(label).add(fb.toString());
+                    }
                 }
             }
-            if (verbose) {
+            if (verbose != null) {
+                for (Entry<String,List<String>> s: output.entrySet()) {
+                    List<String> files = s.getValue();
+                    String key = s.getKey();
+                    if (key.charAt(0) == '-') { // the signature-related group
+                        key = ' ' + key.substring(1);
+                    }
+                    int pipe = key.indexOf('|');
+                    if (verbose.equals("all")) {
+                        for (String f: files) {
+                            System.out.println(key.substring(0, pipe) + f);
+                            System.out.printf(key.substring(pipe+1));
+                        }
+                    } else {
+                        if (verbose.equals("grouped")) {
+                            for (String f: files) {
+                                System.out.println(key.substring(0, pipe) + f);
+                            }
+                        } else if (verbose.equals("summary")) {
+                            System.out.print(key.substring(0, pipe));
+                            if (files.size() > 1) {
+                                System.out.println(files.get(0) + " " +
+                                        String.format(rb.getString(
+                                        "(and %d more)"), files.size()-1));
+                            } else {
+                                System.out.println(files.get(0));
+                            }
+                        }
+                        System.out.printf(key.substring(pipe+1));
+                    }
+                }
                 System.out.println();
                 System.out.println(rb.getString(
                     "  s = signature was verified "));
@@ -589,9 +721,12 @@
                     "  k = at least one certificate was found in keystore"));
                 System.out.println(rb.getString(
                     "  i = at least one certificate was found in identity scope"));
+                if (ckaliases.size() > 0) {
+                    System.out.println((
+                        "  X = not signed by specified alias(es)"));
+                }
                 System.out.println();
             }
-
             if (man == null)
                 System.out.println(rb.getString("no manifest."));
 
@@ -602,7 +737,8 @@
                 System.out.println(rb.getString("jar verified."));
                 if (hasUnsignedEntry || hasExpiredCert || hasExpiringCert ||
                     badKeyUsage || badExtendedKeyUsage || badNetscapeCertType ||
-                    notYetValidCert) {
+                    notYetValidCert || chainNotValidated ||
+                    aliasNotInStore || notSignedByAlias) {
 
                     System.out.println();
                     System.out.println(rb.getString("Warning: "));
@@ -638,14 +774,27 @@
                             "This jar contains entries whose signer certificate is not yet valid. "));
                     }
 
-                    if (! (verbose && showcerts)) {
+                    if (chainNotValidated) {
+                        System.out.println(
+                                rb.getString("This jar contains entries whose certificate chain is not validated."));
+                    }
+
+                    if (notSignedByAlias) {
+                        System.out.println(
+                                rb.getString("This jar contains signed entries which is not signed by the specified alias(es)."));
+                    }
+
+                    if (aliasNotInStore) {
+                        System.out.println(rb.getString("This jar contains signed entries that's not signed by alias in this keystore."));
+                    }
+                    if (! (verbose != null && showcerts)) {
                         System.out.println();
                         System.out.println(rb.getString(
                             "Re-run with the -verbose and -certs options for more details."));
                     }
                 }
             }
-            System.exit(0);
+            return;
         } catch (Exception e) {
             System.out.println(rb.getString("jarsigner: ") + e);
             if (debug) {
@@ -660,15 +809,6 @@
         System.exit(1);
     }
 
-    /*
-     * Display some details about a certificate:
-     *
-     * <cert-type> [", " <subject-DN>] [" (" <keystore-entry-alias> ")"]
-     */
-    String printCert(Certificate c) {
-        return printCert("", c, false, 0);
-    }
-
     private static MessageFormat validityTimeForm = null;
     private static MessageFormat notYetTimeForm = null;
     private static MessageFormat expiredTimeForm = null;
@@ -679,6 +819,8 @@
      *
      * [<tab>] <cert-type> [", " <subject-DN>] [" (" <keystore-entry-alias> ")"]
      * [<validity-period> | <expiry-warning>]
+     *
+     * Note: no newline character at the end
      */
     String printCert(String tab, Certificate c, boolean checkValidityPeriod,
         long now) {
@@ -788,54 +930,75 @@
             .append(signTimeForm.format(source)).append("]").toString();
     }
 
+    private Map<CodeSigner,Integer> cacheForInKS =
+            new IdentityHashMap<CodeSigner,Integer>();
+
+    private int inKeyStoreForOneSigner(CodeSigner signer) {
+        if (cacheForInKS.containsKey(signer)) {
+            return cacheForInKS.get(signer);
+        }
+
+        boolean found = false;
+        int result = 0;
+        List<? extends Certificate> certs = signer.getSignerCertPath().getCertificates();
+        for (Certificate c : certs) {
+            String alias = storeHash.get(c);
+            if (alias != null) {
+                if (alias.startsWith("(")) {
+                    result |= IN_KEYSTORE;
+                } else if (alias.startsWith("[")) {
+                    result |= IN_SCOPE;
+                }
+                if (ckaliases.contains(alias.substring(1, alias.length() - 1))) {
+                    result |= SIGNED_BY_ALIAS;
+                }
+            } else {
+                if (store != null) {
+                    try {
+                        alias = store.getCertificateAlias(c);
+                    } catch (KeyStoreException kse) {
+                        // never happens, because keystore has been loaded
+                    }
+                    if (alias != null) {
+                        storeHash.put(c, "(" + alias + ")");
+                        found = true;
+                        result |= IN_KEYSTORE;
+                    }
+                }
+                if (!found && (scope != null)) {
+                    Identity id = scope.getIdentity(c.getPublicKey());
+                    if (id != null) {
+                        result |= IN_SCOPE;
+                        storeHash.put(c, "[" + id.getName() + "]");
+                    }
+                }
+                if (ckaliases.contains(alias)) {
+                    result |= SIGNED_BY_ALIAS;
+                }
+            }
+        }
+        cacheForInKS.put(signer, result);
+        return result;
+    }
+
     Hashtable<Certificate, String> storeHash =
                                 new Hashtable<Certificate, String>();
 
     int inKeyStore(CodeSigner[] signers) {
-        int result = 0;
 
         if (signers == null)
             return 0;
 
-        boolean found = false;
-
-        for (int i = 0; i < signers.length; i++) {
-            found = false;
-            List<? extends Certificate> certs =
-                signers[i].getSignerCertPath().getCertificates();
-
-            for (Certificate c : certs) {
-                String alias = storeHash.get(c);
+        int output = 0;
 
-                if (alias != null) {
-                    if (alias.startsWith("("))
-                            result |= IN_KEYSTORE;
-                    else if (alias.startsWith("["))
-                            result |= IN_SCOPE;
-                } else {
-                    if (store != null) {
-                        try {
-                            alias = store.getCertificateAlias(c);
-                        } catch (KeyStoreException kse) {
-                            // never happens, because keystore has been loaded
-                        }
-                        if (alias != null) {
-                            storeHash.put(c, "("+alias+")");
-                            found = true;
-                            result |= IN_KEYSTORE;
-                        }
-                    }
-                    if (!found && (scope != null)) {
-                        Identity id = scope.getIdentity(c.getPublicKey());
-                        if (id != null) {
-                            result |= IN_SCOPE;
-                            storeHash.put(c, "["+id.getName()+"]");
-                        }
-                    }
-                }
-            }
+        for (CodeSigner signer: signers) {
+            int result = inKeyStoreForOneSigner(signer);
+            output |= result;
         }
-        return result;
+        if (ckaliases.size() > 0 && (output & SIGNED_BY_ALIAS) == 0) {
+            output |= NOT_ALIAS;
+        }
+        return output;
     }
 
     void signJar(String jarName, String alias, String[] args)
@@ -1025,7 +1188,7 @@
                 // manifest file has new length
                 mfFile = new ZipEntry(JarFile.MANIFEST_NAME);
             }
-            if (verbose) {
+            if (verbose != null) {
                 if (mfCreated) {
                     System.out.println(rb.getString("   adding: ") +
                                         mfFile.getName());
@@ -1076,7 +1239,7 @@
             // signature file
             zos.putNextEntry(sfFile);
             sf.write(zos);
-            if (verbose) {
+            if (verbose != null) {
                 if (zipFile.getEntry(sfFilename) != null) {
                     System.out.println(rb.getString(" updating: ") +
                                 sfFilename);
@@ -1086,7 +1249,7 @@
                 }
             }
 
-            if (verbose) {
+            if (verbose != null) {
                 if (tsaUrl != null || tsaCert != null) {
                     System.out.println(
                         rb.getString("requesting a signature timestamp"));
@@ -1101,8 +1264,8 @@
                         System.out.println(rb.getString("TSA location: ") +
                             certUrl);
                     }
-                    System.out.println(
-                        rb.getString("TSA certificate: ") + printCert(tsaCert));
+                    System.out.println(rb.getString("TSA certificate: ") +
+                        printCert("", tsaCert, false, 0));
                 }
                 if (signingMechanism != null) {
                     System.out.println(
@@ -1113,7 +1276,7 @@
             // signature block file
             zos.putNextEntry(bkFile);
             block.write(zos);
-            if (verbose) {
+            if (verbose != null) {
                 if (zipFile.getEntry(bkFilename) != null) {
                     System.out.println(rb.getString(" updating: ") +
                         bkFilename);
@@ -1140,7 +1303,7 @@
                 ZipEntry ze = enum_.nextElement();
 
                 if (!ze.getName().startsWith(META_INF)) {
-                    if (verbose) {
+                    if (verbose != null) {
                         if (manifest.getAttributes(ze.getName()) != null)
                           System.out.println(rb.getString("  signing: ") +
                                 ze.getName());
@@ -1194,7 +1357,8 @@
             }
 
             if (hasExpiredCert || hasExpiringCert || notYetValidCert
-                    || badKeyUsage || badExtendedKeyUsage || badNetscapeCertType) {
+                    || badKeyUsage || badExtendedKeyUsage
+                    || badNetscapeCertType || chainNotValidated) {
                 System.out.println();
 
                 System.out.println(rb.getString("Warning: "));
@@ -1223,6 +1387,11 @@
                     System.out.println(
                         rb.getString("The signer certificate is not yet valid."));
                 }
+
+                if (chainNotValidated) {
+                    System.out.println(
+                            rb.getString("The signer's certificate chain is not validated."));
+                }
             }
 
         // no IOException thrown in the above try clause, so disable
@@ -1274,6 +1443,40 @@
         return false;
     }
 
+    Map<CodeSigner,String> cacheForSignerInfo = new IdentityHashMap<CodeSigner,String>();
+
+    /**
+     * Returns a string of singer info, with a newline at the end
+     */
+    private String signerInfo(CodeSigner signer, String tab, long now) {
+        if (cacheForSignerInfo.containsKey(signer)) {
+            return cacheForSignerInfo.get(signer);
+        }
+        StringBuffer s = new StringBuffer();
+        List<? extends Certificate> certs = signer.getSignerCertPath().getCertificates();
+        // display the signature timestamp, if present
+        Timestamp timestamp = signer.getTimestamp();
+        if (timestamp != null) {
+            s.append(printTimestamp(tab, timestamp));
+        }
+        // display the certificate(s)
+        for (Certificate c : certs) {
+            s.append(printCert(tab, c, true, now));
+            s.append('\n');
+        }
+        try {
+            CertPath cp = certificateFactory.generateCertPath(certs);
+            validator.validate(cp, pkixParameters);
+        } catch (Exception e) {
+            chainNotValidated = true;
+            s.append(tab + rb.getString("[CertPath not validated: ") +
+                    e.getLocalizedMessage() + "]\n");   // TODO
+        }
+        String result = s.toString();
+        cacheForSignerInfo.put(signer, result);
+        return result;
+    }
+
     private void writeEntry(ZipFile zf, ZipOutputStream os, ZipEntry ze)
     throws IOException
     {
@@ -1360,6 +1563,48 @@
                     }
                 }
             }
+            Set<TrustAnchor> tas = new HashSet<TrustAnchor>();
+            try {
+                KeyStore caks = KeyTool.getCacertsKeyStore();
+                if (caks != null) {
+                    Enumeration<String> aliases = caks.aliases();
+                    while (aliases.hasMoreElements()) {
+                        String a = aliases.nextElement();
+                        try {
+                            tas.add(new TrustAnchor((X509Certificate)caks.getCertificate(a), null));
+                        } catch (Exception e2) {
+                            // ignore, when a SecretkeyEntry does not include a cert
+                        }
+                    }
+                }
+            } catch (Exception e) {
+                // Ignore, if cacerts cannot be loaded
+            }
+            if (store != null) {
+                Enumeration<String> aliases = store.aliases();
+                while (aliases.hasMoreElements()) {
+                    String a = aliases.nextElement();
+                    try {
+                        X509Certificate c = (X509Certificate)store.getCertificate(a);
+                        // Only add TrustedCertificateEntry and self-signed
+                        // PrivateKeyEntry
+                        if (store.isCertificateEntry(a) ||
+                                c.getSubjectDN().equals(c.getIssuerDN())) {
+                            tas.add(new TrustAnchor(c, null));
+                        }
+                    } catch (Exception e2) {
+                        // ignore, when a SecretkeyEntry does not include a cert
+                    }
+                }
+            }
+            certificateFactory = CertificateFactory.getInstance("X.509");
+            validator = CertPathValidator.getInstance("PKIX");
+            try {
+                pkixParameters = new PKIXParameters(tas);
+                pkixParameters.setRevocationEnabled(false);
+            } catch (InvalidAlgorithmParameterException ex) {
+                // Only if tas is empty
+            }
         } catch (IOException ioe) {
             throw new RuntimeException(rb.getString("keystore load: ") +
                                         ioe.getMessage());
@@ -1408,7 +1653,8 @@
     void checkCertUsage(X509Certificate userCert, boolean[] bad) {
 
         // Can act as a signer?
-        // 1. if KeyUsage, then [0] should be true
+        // 1. if KeyUsage, then [0:digitalSignature] or
+        //    [1:nonRepudiation] should be true
         // 2. if ExtendedKeyUsage, then should contains ANY or CODE_SIGNING
         // 3. if NetscapeCertType, then should contains OBJECT_SIGNING
         // 1,2,3 must be true
@@ -1419,10 +1665,10 @@
 
         boolean[] keyUsage = userCert.getKeyUsage();
         if (keyUsage != null) {
-            if (keyUsage.length < 1 || !keyUsage[0]) {
+            keyUsage = Arrays.copyOf(keyUsage, 9);
+            if (!keyUsage[0] && !keyUsage[1]) {
                 if (bad != null) {
                     bad[0] = true;
-                } else {
                     badKeyUsage = true;
                 }
             }
@@ -1435,7 +1681,6 @@
                         && !xKeyUsage.contains("1.3.6.1.5.5.7.3.3")) {  // codeSigning
                     if (bad != null) {
                         bad[1] = true;
-                    } else {
                         badExtendedKeyUsage = true;
                     }
                 }
@@ -1462,7 +1707,6 @@
                 if (!val) {
                     if (bad != null) {
                         bad[2] = true;
-                    } else {
                         badNetscapeCertType = true;
                     }
                 }
@@ -1477,19 +1721,36 @@
         Key key = null;
 
         try {
-
             java.security.cert.Certificate[] cs = null;
-
-            try {
-                cs = store.getCertificateChain(alias);
-            } catch (KeyStoreException kse) {
-                // this never happens, because keystore has been loaded
+            if (altCertChain != null) {
+                try {
+                    cs = CertificateFactory.getInstance("X.509").
+                            generateCertificates(new FileInputStream(altCertChain)).
+                            toArray(new Certificate[0]);
+                } catch (CertificateException ex) {
+                    error(rb.getString("Cannot restore certchain from file specified"));
+                } catch (FileNotFoundException ex) {
+                    error(rb.getString("File specified by -certchain does not exist"));
+                }
+            } else {
+                try {
+                    cs = store.getCertificateChain(alias);
+                } catch (KeyStoreException kse) {
+                    // this never happens, because keystore has been loaded
+                }
             }
-            if (cs == null) {
-                MessageFormat form = new MessageFormat(rb.getString
-                    ("Certificate chain not found for: alias.  alias must reference a valid KeyStore key entry containing a private key and corresponding public key certificate chain."));
-                Object[] source = {alias, alias};
-                error(form.format(source));
+            if (cs == null || cs.length == 0) {
+                if (altCertChain != null) {
+                    error(rb.getString
+                            ("Certificate chain not found in the file specified."));
+                } else {
+                    MessageFormat form = new MessageFormat(rb.getString
+                        ("Certificate chain not found for: alias.  alias must" +
+                        " reference a valid KeyStore key entry containing a" +
+                        " private key and corresponding public key certificate chain."));
+                    Object[] source = {alias, alias};
+                    error(form.format(source));
+                }
             }
 
             certChain = new X509Certificate[cs.length];
@@ -1501,56 +1762,15 @@
                 certChain[i] = (X509Certificate)cs[i];
             }
 
-            // order the cert chain if necessary (put user cert first,
-            // root-cert last in the chain)
-            X509Certificate userCert
-                = (X509Certificate)store.getCertificate(alias);
-
-            // check validity of signer certificate
-            try {
-                userCert.checkValidity();
-
-                if (userCert.getNotAfter().getTime() <
-                    System.currentTimeMillis() + SIX_MONTHS) {
-
-                    hasExpiringCert = true;
-                }
-            } catch (CertificateExpiredException cee) {
-                hasExpiredCert = true;
-
-            } catch (CertificateNotYetValidException cnyve) {
-                notYetValidCert = true;
-            }
-
-            checkCertUsage(userCert, null);
+            // We don't meant to print anything, the next call
+            // checks validity and keyUsage etc
+            printCert("", certChain[0], true, 0);
 
-            if (!userCert.equals(certChain[0])) {
-                // need to order ...
-                X509Certificate[] certChainTmp
-                    = new X509Certificate[certChain.length];
-                certChainTmp[0] = userCert;
-                Principal issuer = userCert.getIssuerDN();
-                for (int i=1; i<certChain.length; i++) {
-                    int j;
-                    // look for the cert whose subject corresponds to the
-                    // given issuer
-                    for (j=0; j<certChainTmp.length; j++) {
-                        if (certChainTmp[j] == null)
-                            continue;
-                        Principal subject = certChainTmp[j].getSubjectDN();
-                        if (issuer.equals(subject)) {
-                            certChain[i] = certChainTmp[j];
-                            issuer = certChainTmp[j].getIssuerDN();
-                            certChainTmp[j] = null;
-                            break;
-                        }
-                    }
-                    if (j == certChainTmp.length) {
-                        error(rb.getString("incomplete certificate chain"));
-                    }
-
-                }
-                certChain = certChainTmp; // ordered
+            try {
+                CertPath cp = certificateFactory.generateCertPath(Arrays.asList(certChain));
+                validator.validate(cp, pkixParameters);
+            } catch (Exception e) {
+                chainNotValidated = true;
             }
 
             try {
--- a/jdk/src/share/classes/sun/security/tools/JarSignerResources.java	Thu Apr 02 16:51:55 2009 -0700
+++ b/jdk/src/share/classes/sun/security/tools/JarSignerResources.java	Fri Apr 03 10:29:13 2009 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright 2000-2005 Sun Microsystems, Inc.  All Rights Reserved.
+ * Copyright 2000-2009 Sun Microsystems, Inc.  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
@@ -54,8 +54,8 @@
                  "If keystore is not password protected, then -storepass and -keypass must not be specified"},
         {"Usage: jarsigner [options] jar-file alias",
                 "Usage: jarsigner [options] jar-file alias"},
-        {"       jarsigner -verify [options] jar-file",
-                "       jarsigner -verify [options] jar-file"},
+        {"       jarsigner -verify [options] jar-file [alias...]",
+                "       jarsigner -verify [options] jar-file [alias...]"},
         {"[-keystore <url>]           keystore location",
                 "[-keystore <url>]           keystore location"},
         {"[-storepass <password>]     password for keystore integrity",
@@ -64,6 +64,8 @@
                 "[-storetype <type>]         keystore type"},
         {"[-keypass <password>]       password for private key (if different)",
                 "[-keypass <password>]       password for private key (if different)"},
+        {"[-certchain <file>]         name of alternative certchain file",
+                "[-certchain <file>]         name of alternative certchain file"},
         {"[-sigfile <file>]           name of .SF/.DSA file",
                 "[-sigfile <file>]           name of .SF/.DSA file"},
         {"[-signedjar <file>]         name of signed JAR file",
@@ -74,8 +76,10 @@
                 "[-sigalg <algorithm>]       name of signature algorithm"},
         {"[-verify]                   verify a signed JAR file",
                 "[-verify]                   verify a signed JAR file"},
-        {"[-verbose]                  verbose output when signing/verifying",
-                "[-verbose]                  verbose output when signing/verifying"},
+        {"[-verbose[:suboptions]]     verbose output when signing/verifying.",
+                "[-verbose[:suboptions]]     verbose output when signing/verifying."},
+        {"                            suboptions can be all, grouped or summary",
+                "                            suboptions can be all, grouped or summary"},
         {"[-certs]                    display certificates when verbose and verifying",
                 "[-certs]                    display certificates when verbose and verifying"},
         {"[-tsa <url>]                location of the Timestamping Authority",
@@ -98,10 +102,22 @@
                 "[-providerClass <class>     name of cryptographic service provider's"},
         {"  [-providerArg <arg>]] ... master class file and constructor argument",
                 "  [-providerArg <arg>]] ... master class file and constructor argument"},
+        {"[-strict]                   treat warnings as errors",
+                "[-strict]                   treat warnings as errors"},
+        {"Option lacks argument", "Option lacks argument"},
+        {"Please type jarsigner -help for usage", "Please type jarsigner -help for usage"},
+        {"Please specify jarfile name", "Please specify jarfile name"},
+        {"Please specify alias name", "Please specify alias name"},
+        {"Only one alias can be specified", "Only one alias can be specified"},
+        {"This jar contains signed entries which is not signed by the specified alias(es).",
+                 "This jar contains signed entries which is not signed by the specified alias(es)."},
+        {"This jar contains signed entries that's not signed by alias in this keystore.",
+                  "This jar contains signed entries that's not signed by alias in this keystore."},
         {"s", "s"},
         {"m", "m"},
         {"k", "k"},
         {"i", "i"},
+        {"(and %d more)", "(and %d more)"},
         {"  s = signature was verified ",
                 "  s = signature was verified "},
         {"  m = entry is listed in manifest",
@@ -110,7 +126,11 @@
                 "  k = at least one certificate was found in keystore"},
         {"  i = at least one certificate was found in identity scope",
                 "  i = at least one certificate was found in identity scope"},
+        {"  X = not signed by specified alias(es)",
+                "  X = not signed by specified alias(es)"},
         {"no manifest.", "no manifest."},
+        {"(Signature related entries)","(Signature related entries)"},
+        {"(Unsigned entries)", "(Unsigned entries)"},
         {"jar is unsigned. (signatures missing or not parsable)",
                 "jar is unsigned. (signatures missing or not parsable)"},
         {"jar verified.", "jar verified."},
@@ -134,6 +154,12 @@
                 "unable to instantiate keystore class: "},
         {"Certificate chain not found for: alias.  alias must reference a valid KeyStore key entry containing a private key and corresponding public key certificate chain.",
                 "Certificate chain not found for: {0}.  {1} must reference a valid KeyStore key entry containing a private key and corresponding public key certificate chain."},
+        {"File specified by -certchain does not exist",
+                "File specified by -certchain does not exist"},
+        {"Cannot restore certchain from file specified",
+                "Cannot restore certchain from file specified"},
+        {"Certificate chain not found in the file specified.",
+                "Certificate chain not found in the file specified."},
         {"found non-X.509 certificate in signer's chain",
                 "found non-X.509 certificate in signer's chain"},
         {"incomplete certificate chain", "incomplete certificate chain"},
@@ -149,6 +175,7 @@
         {"certificate is not valid until",
                 "certificate is not valid until {0}"},
         {"certificate will expire on", "certificate will expire on {0}"},
+        {"[CertPath not validated: ", "[CertPath not validated: "},
         {"requesting a signature timestamp",
                 "requesting a signature timestamp"},
         {"TSA location: ", "TSA location: "},
@@ -189,14 +216,18 @@
                  "The signer certificate's ExtendedKeyUsage extension doesn't allow code signing."},
         {"The signer certificate's NetscapeCertType extension doesn't allow code signing.",
                  "The signer certificate's NetscapeCertType extension doesn't allow code signing."},
-         {"This jar contains entries whose signer certificate's KeyUsage extension doesn't allow code signing.",
-                  "This jar contains entries whose signer certificate's KeyUsage extension doesn't allow code signing."},
-         {"This jar contains entries whose signer certificate's ExtendedKeyUsage extension doesn't allow code signing.",
-                  "This jar contains entries whose signer certificate's ExtendedKeyUsage extension doesn't allow code signing."},
-         {"This jar contains entries whose signer certificate's NetscapeCertType extension doesn't allow code signing.",
-                  "This jar contains entries whose signer certificate's NetscapeCertType extension doesn't allow code signing."},
+        {"This jar contains entries whose signer certificate's KeyUsage extension doesn't allow code signing.",
+                 "This jar contains entries whose signer certificate's KeyUsage extension doesn't allow code signing."},
+        {"This jar contains entries whose signer certificate's ExtendedKeyUsage extension doesn't allow code signing.",
+                 "This jar contains entries whose signer certificate's ExtendedKeyUsage extension doesn't allow code signing."},
+        {"This jar contains entries whose signer certificate's NetscapeCertType extension doesn't allow code signing.",
+                 "This jar contains entries whose signer certificate's NetscapeCertType extension doesn't allow code signing."},
         {"[{0} extension does not support code signing]",
                  "[{0} extension does not support code signing]"},
+        {"The signer's certificate chain is not validated.",
+                "The signer's certificate chain is not validated."},
+        {"This jar contains entries whose certificate chain is not validated.",
+                 "This jar contains entries whose certificate chain is not validated."},
     };
 
     /**
--- a/jdk/src/share/classes/sun/security/tools/KeyTool.java	Thu Apr 02 16:51:55 2009 -0700
+++ b/jdk/src/share/classes/sun/security/tools/KeyTool.java	Fri Apr 03 10:29:13 2009 -0700
@@ -2545,7 +2545,19 @@
      * Returns true if the certificate is self-signed, false otherwise.
      */
     private boolean isSelfSigned(X509Certificate cert) {
-        return cert.getSubjectDN().equals(cert.getIssuerDN());
+        return signedBy(cert, cert);
+    }
+
+    private boolean signedBy(X509Certificate end, X509Certificate ca) {
+        if (!ca.getSubjectDN().equals(end.getIssuerDN())) {
+            return false;
+        }
+        try {
+            end.verify(ca.getPublicKey());
+            return true;
+        } catch (Exception e) {
+            return false;
+        }
     }
 
     /**
@@ -2869,20 +2881,18 @@
         Certificate tmpCert = replyCerts[0];
         replyCerts[0] = replyCerts[i];
         replyCerts[i] = tmpCert;
-        Principal issuer = ((X509Certificate)replyCerts[0]).getIssuerDN();
+
+        X509Certificate thisCert = (X509Certificate)replyCerts[0];
 
         for (i=1; i < replyCerts.length-1; i++) {
-            // find a cert in the reply whose "subject" is the same as the
-            // given "issuer"
+            // find a cert in the reply who signs thisCert
             int j;
             for (j=i; j<replyCerts.length; j++) {
-                Principal subject;
-                subject = ((X509Certificate)replyCerts[j]).getSubjectDN();
-                if (subject.equals(issuer)) {
+                if (signedBy(thisCert, (X509Certificate)replyCerts[j])) {
                     tmpCert = replyCerts[i];
                     replyCerts[i] = replyCerts[j];
                     replyCerts[j] = tmpCert;
-                    issuer = ((X509Certificate)replyCerts[i]).getIssuerDN();
+                    thisCert = (X509Certificate)replyCerts[i];
                     break;
                 }
             }
@@ -2892,18 +2902,6 @@
             }
         }
 
-        // now verify each cert in the ordered chain
-        for (i=0; i<replyCerts.length-1; i++) {
-            PublicKey pubKey = replyCerts[i+1].getPublicKey();
-            try {
-                replyCerts[i].verify(pubKey);
-            } catch (Exception e) {
-                throw new Exception(rb.getString
-                        ("Certificate chain in reply does not verify: ") +
-                        e.getMessage());
-            }
-        }
-
         if (noprompt) {
             return replyCerts;
         }
@@ -3035,9 +3033,8 @@
     private boolean buildChain(X509Certificate certToVerify,
                         Vector<Certificate> chain,
                         Hashtable<Principal, Vector<Certificate>> certs) {
-        Principal subject = certToVerify.getSubjectDN();
         Principal issuer = certToVerify.getIssuerDN();
-        if (subject.equals(issuer)) {
+        if (isSelfSigned(certToVerify)) {
             // reached self-signed root cert;
             // no verification needed because it's trusted.
             chain.addElement(certToVerify);
@@ -3108,7 +3105,7 @@
     /**
      * Returns the keystore with the configured CA certificates.
      */
-    private KeyStore getCacertsKeyStore()
+    public static KeyStore getCacertsKeyStore()
         throws Exception
     {
         String sep = File.separator;
--- a/jdk/src/share/classes/sun/security/util/ManifestEntryVerifier.java	Thu Apr 02 16:51:55 2009 -0700
+++ b/jdk/src/share/classes/sun/security/util/ManifestEntryVerifier.java	Fri Apr 03 10:29:13 2009 -0700
@@ -44,8 +44,6 @@
 
     private static final Debug debug = Debug.getInstance("jar");
 
-    private static final Provider digestProvider = Providers.getSunProvider();
-
     /** the created digest objects */
     HashMap<String, MessageDigest> createdDigests;
 
@@ -127,7 +125,7 @@
                     try {
 
                         digest = MessageDigest.getInstance
-                                        (algorithm, digestProvider);
+                                        (algorithm, Providers.getSunProvider());
                         createdDigests.put(algorithm, digest);
                     } catch (NoSuchAlgorithmException nsae) {
                         // ignore
--- a/jdk/src/share/native/java/util/zip/zip_util.c	Thu Apr 02 16:51:55 2009 -0700
+++ b/jdk/src/share/native/java/util/zip/zip_util.c	Fri Apr 03 10:29:13 2009 -0700
@@ -313,6 +313,38 @@
 }
 
 /*
+ * Searches for the ZIP64 end of central directory (END) header. The
+ * contents of the ZIP64 END header will be read and placed in end64buf.
+ * Returns the file position of the ZIP64 END header, otherwise returns
+ * -1 if the END header was not found or an error occurred.
+ *
+ * The ZIP format specifies the "position" of each related record as
+ *   ...
+ *   [central directory]
+ *   [zip64 end of central directory record]
+ *   [zip64 end of central directory locator]
+ *   [end of central directory record]
+ *
+ * The offset of zip64 end locator can be calculated from endpos as
+ * "endpos - ZIP64_LOCHDR".
+ * The "offset" of zip64 end record is stored in zip64 end locator.
+ */
+static jlong
+findEND64(jzfile *zip, void *end64buf, jlong endpos)
+{
+    char loc64[ZIP64_LOCHDR];
+    jlong end64pos;
+    if (readFullyAt(zip->zfd, loc64, ZIP64_LOCHDR, endpos - ZIP64_LOCHDR) == -1) {
+        return -1;    // end64 locator not found
+    }
+    end64pos = ZIP64_LOCOFF(loc64);
+    if (readFullyAt(zip->zfd, end64buf, ZIP64_ENDHDR, end64pos) == -1) {
+        return -1;    // end64 record not found
+    }
+    return end64pos;
+}
+
+/*
  * Returns a hash code value for a C-style NUL-terminated string.
  */
 static unsigned int
@@ -463,7 +495,7 @@
 readCEN(jzfile *zip, jint knownTotal)
 {
     /* Following are unsigned 32-bit */
-    jlong endpos, cenpos, cenlen;
+    jlong endpos, end64pos, cenpos, cenlen, cenoff;
     /* Following are unsigned 16-bit */
     jint total, tablelen, i, j;
     unsigned char *cenbuf = NULL;
@@ -474,6 +506,7 @@
     jlong offset;
 #endif
     unsigned char endbuf[ENDHDR];
+    jint endhdrlen = ENDHDR;
     jzcell *entries;
     jint *table;
 
@@ -490,13 +523,27 @@
 
    /* Get position and length of central directory */
     cenlen = ENDSIZ(endbuf);
+    cenoff = ENDOFF(endbuf);
+    total  = ENDTOT(endbuf);
+    if (cenlen == ZIP64_MAGICVAL || cenoff == ZIP64_MAGICVAL ||
+        total == ZIP64_MAGICCOUNT) {
+        unsigned char end64buf[ZIP64_ENDHDR];
+        if ((end64pos = findEND64(zip, end64buf, endpos)) != -1) {
+            cenlen = ZIP64_ENDSIZ(end64buf);
+            cenoff = ZIP64_ENDOFF(end64buf);
+            total = (jint)ZIP64_ENDTOT(end64buf);
+            endpos = end64pos;
+            endhdrlen = ZIP64_ENDHDR;
+        }
+    }
+
     if (cenlen > endpos)
         ZIP_FORMAT_ERROR("invalid END header (bad central directory size)");
     cenpos = endpos - cenlen;
 
     /* Get position of first local file (LOC) header, taking into
      * account that there may be a stub prefixed to the zip file. */
-    zip->locpos = cenpos - ENDOFF(endbuf);
+    zip->locpos = cenpos - cenoff;
     if (zip->locpos < 0)
         ZIP_FORMAT_ERROR("invalid END header (bad central directory offset)");
 
@@ -527,7 +574,7 @@
            out the page size in order to make offset to be multiples of
            page size.
         */
-        zip->mlen = cenpos - offset + cenlen + ENDHDR;
+        zip->mlen = cenpos - offset + cenlen + endhdrlen;
         zip->offset = offset;
         mappedAddr = mmap64(0, zip->mlen, PROT_READ, MAP_SHARED, zip->zfd, (off64_t) offset);
         zip->maddr = (mappedAddr == (void*) MAP_FAILED) ? NULL :
@@ -551,8 +598,13 @@
      * is a 2-byte field, but we (and other zip implementations)
      * support approx. 2**31 entries, we do not trust ENDTOT, but
      * treat it only as a strong hint.  When we call ourselves
-     * recursively, knownTotal will have the "true" value. */
-    total = (knownTotal != -1) ? knownTotal : ENDTOT(endbuf);
+     * recursively, knownTotal will have the "true" value.
+     *
+     * Keep this path alive even with the Zip64 END support added, just
+     * for zip files that have more than 0xffff entries but don't have
+     * the Zip64 enabled.
+     */
+    total = (knownTotal != -1) ? knownTotal : total;
     entries  = zip->entries  = calloc(total, sizeof(entries[0]));
     tablelen = zip->tablelen = ((total/2) | 1); // Odd -> fewer collisions
     table    = zip->table    = malloc(tablelen * sizeof(table[0]));
@@ -854,6 +906,7 @@
 static jzentry *
 newEntry(jzfile *zip, jzcell *zc, AccessHint accessHint)
 {
+    jlong locoff;
     jint nlen, elen, clen;
     jzentry *ze;
     char *cen;
@@ -880,18 +933,55 @@
     ze->size  = CENLEN(cen);
     ze->csize = (CENHOW(cen) == STORED) ? 0 : CENSIZ(cen);
     ze->crc   = CENCRC(cen);
-    ze->pos   = -(zip->locpos + CENOFF(cen));
+    locoff    = CENOFF(cen);
+    ze->pos   = -(zip->locpos + locoff);
 
     if ((ze->name = malloc(nlen + 1)) == NULL) goto Catch;
     memcpy(ze->name, cen + CENHDR, nlen);
     ze->name[nlen] = '\0';
 
     if (elen > 0) {
+        char *extra = cen + CENHDR + nlen;
+
         /* This entry has "extra" data */
         if ((ze->extra = malloc(elen + 2)) == NULL) goto Catch;
         ze->extra[0] = (unsigned char) elen;
         ze->extra[1] = (unsigned char) (elen >> 8);
-        memcpy(ze->extra+2, cen + CENHDR + nlen, elen);
+        memcpy(ze->extra+2, extra, elen);
+        if (ze->csize == ZIP64_MAGICVAL || ze->size == ZIP64_MAGICVAL ||
+            locoff == ZIP64_MAGICVAL) {
+            jint off = 0;
+            while ((off + 4) < elen) {    // spec: HeaderID+DataSize+Data
+                jint sz = SH(extra, off + 2);
+                if (SH(extra, off) == ZIP64_EXTID) {
+                    off += 4;
+                    if (ze->size == ZIP64_MAGICVAL) {
+                        // if invalid zip64 extra fields, just skip
+                        if (sz < 8 || (off + 8) > elen)
+                            break;
+                        ze->size = LL(extra, off);
+                        sz -= 8;
+                        off += 8;
+                    }
+                    if (ze->csize == ZIP64_MAGICVAL) {
+                        if (sz < 8 || (off + 8) > elen)
+                            break;
+                        ze->csize = LL(extra, off);
+                        sz -= 8;
+                        off += 8;
+                    }
+                    if (locoff == ZIP64_MAGICVAL) {
+                        if (sz < 8 || (off + 8) > elen)
+                            break;
+                        ze->pos = -(zip->locpos +  LL(extra, off));
+                        sz -= 8;
+                        off += 8;
+                    }
+                    break;
+                }
+                off += (sz + 4);
+            }
+        }
     }
 
     if (clen > 0) {
--- a/jdk/src/share/native/java/util/zip/zip_util.h	Thu Apr 02 16:51:55 2009 -0700
+++ b/jdk/src/share/native/java/util/zip/zip_util.h	Fri Apr 03 10:29:13 2009 -0700
@@ -38,9 +38,13 @@
 #define CENSIG 0x02014b50L          /* "PK\001\002" */
 #define ENDSIG 0x06054b50L          /* "PK\005\006" */
 
+#define ZIP64_ENDSIG 0x06064b50L    /* "PK\006\006" */
+#define ZIP64_LOCSIG 0x07064b50L    /* "PK\006\007" */
+
 /*
  * Header sizes including signatures
  */
+
 #ifdef USE_MMAP
 #define SIGSIZ  4
 #endif
@@ -49,12 +53,22 @@
 #define CENHDR 46
 #define ENDHDR 22
 
+#define ZIP64_ENDHDR 56       // ZIP64 end header size
+#define ZIP64_LOCHDR 20       // ZIP64 end loc header size
+#define ZIP64_EXTHDR 24       // EXT header size
+#define ZIP64_EXTID   1       // Extra field Zip64 header ID
+
+#define ZIP64_MAGICVAL 0xffffffffLL
+#define ZIP64_MAGICCOUNT 0xffff
+
+
 /*
  * Header field access macros
  */
 #define CH(b, n) (((unsigned char *)(b))[n])
 #define SH(b, n) (CH(b, n) | (CH(b, n+1) << 8))
-#define LG(b, n) (SH(b, n) | (SH(b, n+2) << 16))
+#define LG(b, n) ((SH(b, n) | (SH(b, n+2) << 16)) &0xffffffffUL)
+#define LL(b, n) (((jlong)LG(b, n)) | (((jlong)LG(b, n+4)) << 32))
 #define GETSIG(b) LG(b, 0)
 
 /*
@@ -106,6 +120,26 @@
 #define ENDCOM(b) SH(b, 20)         /* size of zip file comment */
 
 /*
+ * Macros for getting Zip64 end of central directory header fields
+ */
+#define ZIP64_ENDLEN(b) LL(b, 4)      /* size of zip64 end of central dir */
+#define ZIP64_ENDVEM(b) SH(b, 12)     /* version made by */
+#define ZIP64_ENDVER(b) SH(b, 14)     /* version needed to extract */
+#define ZIP64_ENDNMD(b) LG(b, 16)     /* number of this disk */
+#define ZIP64_ENDDSK(b) LG(b, 20)     /* disk number of start */
+#define ZIP64_ENDTOD(b) LL(b, 24)     /* total number of entries on this disk */
+#define ZIP64_ENDTOT(b) LL(b, 32)     /* total number of entries */
+#define ZIP64_ENDSIZ(b) LL(b, 40)     /* central directory size in bytes */
+#define ZIP64_ENDOFF(b) LL(b, 48)     /* offset of first CEN header */
+
+/*
+ * Macros for getting Zip64 end of central directory locator fields
+ */
+#define ZIP64_LOCDSK(b) LG(b, 4)      /* disk number start */
+#define ZIP64_LOCOFF(b) LL(b, 8)      /* offset of zip64 end */
+#define ZIP64_LOCTOT(b) LG(b, 16)     /* total number of disks */
+
+/*
  * Supported compression methods
  */
 #define STORED      0
@@ -145,7 +179,7 @@
  */
 typedef struct jzcell {
     unsigned int hash;    /* 32 bit hashcode on name */
-    unsigned int cenpos;  /* Offset of central directory file header */
+    jlong cenpos;         /* Offset of central directory file header */
     unsigned int next;    /* hash chain: index into jzfile->entries */
 } jzcell;
 
--- a/jdk/src/share/native/java/util/zip/zlib-1.1.3/zlib.h	Thu Apr 02 16:51:55 2009 -0700
+++ b/jdk/src/share/native/java/util/zip/zlib-1.1.3/zlib.h	Fri Apr 03 10:29:13 2009 -0700
@@ -106,11 +106,11 @@
 typedef struct z_stream_s {
     Bytef    *next_in;  /* next input byte */
     uInt     avail_in;  /* number of bytes available at next_in */
-    uLong    total_in;  /* total nb of input bytes read so far */
+    long long total_in;  /* total nb of input bytes read so far */
 
     Bytef    *next_out; /* next output byte should be put there */
     uInt     avail_out; /* remaining free space at next_out */
-    uLong    total_out; /* total nb of bytes output so far */
+    long long total_out; /* total nb of bytes output so far */
 
     char     *msg;      /* last error message, NULL if no error */
     struct internal_state FAR *state; /* not visible by applications */
--- a/jdk/src/solaris/classes/sun/nio/ch/EPollArrayWrapper.java	Thu Apr 02 16:51:55 2009 -0700
+++ b/jdk/src/solaris/classes/sun/nio/ch/EPollArrayWrapper.java	Fri Apr 03 10:29:13 2009 -0700
@@ -78,8 +78,8 @@
     // Base address of the native pollArray
     private final long pollArrayAddress;
 
-    // Set of "idle" file descriptors
-    private final HashSet<Integer> idleSet;
+    // Set of "idle" channels
+    private final HashSet<SelChImpl> idleSet;
 
     EPollArrayWrapper() {
         // creates the epoll file descriptor
@@ -96,19 +96,22 @@
         }
 
         // create idle set
-        idleSet = new HashSet<Integer>();
+        idleSet = new HashSet<SelChImpl>();
     }
 
     // Used to update file description registrations
     private static class Updator {
+        SelChImpl channel;
         int opcode;
-        int fd;
         int events;
-        Updator(int opcode, int fd, int events) {
+        Updator(SelChImpl channel, int opcode, int events) {
+            this.channel = channel;
             this.opcode = opcode;
-            this.fd = fd;
             this.events = events;
         }
+        Updator(SelChImpl channel, int opcode) {
+            this(channel, opcode, 0);
+        }
     }
 
     private LinkedList<Updator> updateList = new LinkedList<Updator>();
@@ -163,60 +166,54 @@
     }
 
     /**
-     * Update the events for a given file descriptor.
+     * Update the events for a given channel.
      */
-    void setInterest(int fd, int mask) {
+    void setInterest(SelChImpl channel, int mask) {
         synchronized (updateList) {
-
-            // if the interest events are 0 then add to idle set, and delete
-            // from epoll if registered (or pending)
-            if (mask == 0) {
-                if (idleSet.add(fd)) {
-                    updateList.add(new Updator(EPOLL_CTL_DEL, fd, 0));
-                }
-                return;
-            }
-
-            // if file descriptor is idle then add to epoll
-            if (!idleSet.isEmpty() && idleSet.remove(fd)) {
-                updateList.add(new Updator(EPOLL_CTL_ADD, fd, mask));
-                return;
-            }
-
             // if the previous pending operation is to add this file descriptor
             // to epoll then update its event set
             if (updateList.size() > 0) {
                 Updator last = updateList.getLast();
-                if (last.fd == fd && last.opcode == EPOLL_CTL_ADD) {
+                if (last.channel == channel && last.opcode == EPOLL_CTL_ADD) {
                     last.events = mask;
                     return;
                 }
             }
 
             // update existing registration
-            updateList.add(new Updator(EPOLL_CTL_MOD, fd, mask));
+            updateList.add(new Updator(channel, EPOLL_CTL_MOD, mask));
+        }
+    }
+
+    /**
+     * Add a channel's file descriptor to epoll
+     */
+    void add(SelChImpl channel) {
+        synchronized (updateList) {
+            updateList.add(new Updator(channel, EPOLL_CTL_ADD));
         }
     }
 
     /**
-     * Add a new file descriptor to epoll
+     * Remove a channel's file descriptor from epoll
      */
-    void add(int fd) {
+    void release(SelChImpl channel) {
         synchronized (updateList) {
-            updateList.add(new Updator(EPOLL_CTL_ADD, fd, 0));
-        }
-    }
+            // flush any pending updates
+            int i = 0;
+            while (i < updateList.size()) {
+                if (updateList.get(i).channel == channel) {
+                    updateList.remove(i);
+                } else {
+                    i++;
+                }
+            }
 
-    /**
-     * Remove a file descriptor from epoll
-     */
-    void release(int fd) {
-        synchronized (updateList) {
-            // if file descriptor is idle then remove from idle set, otherwise
-            // delete from epoll
-            if (!idleSet.remove(fd)) {
-                updateList.add(new Updator(EPOLL_CTL_DEL, fd, 0));
-            }
+            // remove from the idle set (if present)
+            idleSet.remove(channel);
+
+            // remove from epoll (if registered)
+            epollCtl(epfd, EPOLL_CTL_DEL, channel.getFDVal(), 0);
         }
     }
 
@@ -248,7 +245,26 @@
         synchronized (updateList) {
             Updator u = null;
             while ((u = updateList.poll()) != null) {
-                epollCtl(epfd, u.opcode, u.fd, u.events);
+                SelChImpl ch = u.channel;
+                if (!ch.isOpen())
+                    continue;
+
+                // if the events are 0 then file descriptor is put into "idle
+                // set" to prevent it being polled
+                if (u.events == 0) {
+                    boolean added = idleSet.add(u.channel);
+                    // if added to idle set then remove from epoll if registered
+                    if (added && (u.opcode == EPOLL_CTL_MOD))
+                        epollCtl(epfd, EPOLL_CTL_DEL, ch.getFDVal(), 0);
+                } else {
+                    // events are specified. If file descriptor was in idle set
+                    // it must be re-registered (by converting opcode to ADD)
+                    boolean idle = false;
+                    if (!idleSet.isEmpty())
+                        idle = idleSet.remove(u.channel);
+                    int opcode = (idle) ? EPOLL_CTL_ADD : u.opcode;
+                    epollCtl(epfd, opcode, ch.getFDVal(), u.events);
+                }
             }
         }
     }
--- a/jdk/src/solaris/classes/sun/nio/ch/EPollSelectorImpl.java	Thu Apr 02 16:51:55 2009 -0700
+++ b/jdk/src/solaris/classes/sun/nio/ch/EPollSelectorImpl.java	Fri Apr 03 10:29:13 2009 -0700
@@ -139,7 +139,6 @@
         FileDispatcherImpl.closeIntFD(fd0);
         FileDispatcherImpl.closeIntFD(fd1);
 
-        pollWrapper.release(fd0);
         pollWrapper.closeEPollFD();
         // it is possible
         selectedKeys = null;
@@ -162,17 +161,18 @@
     protected void implRegister(SelectionKeyImpl ski) {
         if (closed)
             throw new ClosedSelectorException();
-        int fd = IOUtil.fdVal(ski.channel.getFD());
-        fdToKey.put(Integer.valueOf(fd), ski);
-        pollWrapper.add(fd);
+        SelChImpl ch = ski.channel;
+        fdToKey.put(Integer.valueOf(ch.getFDVal()), ski);
+        pollWrapper.add(ch);
         keys.add(ski);
     }
 
     protected void implDereg(SelectionKeyImpl ski) throws IOException {
         assert (ski.getIndex() >= 0);
-        int fd = ski.channel.getFDVal();
+        SelChImpl ch = ski.channel;
+        int fd = ch.getFDVal();
         fdToKey.remove(Integer.valueOf(fd));
-        pollWrapper.release(fd);
+        pollWrapper.release(ch);
         ski.setIndex(-1);
         keys.remove(ski);
         selectedKeys.remove(ski);
@@ -185,8 +185,7 @@
     void putEventOps(SelectionKeyImpl sk, int ops) {
         if (closed)
             throw new ClosedSelectorException();
-        int fd = IOUtil.fdVal(sk.channel.getFD());
-        pollWrapper.setInterest(fd, ops);
+        pollWrapper.setInterest(sk.channel, ops);
     }
 
     public Selector wakeup() {
--- a/jdk/src/solaris/native/sun/nio/ch/DevPollArrayWrapper.c	Thu Apr 02 16:51:55 2009 -0700
+++ b/jdk/src/solaris/native/sun/nio/ch/DevPollArrayWrapper.c	Fri Apr 03 10:29:13 2009 -0700
@@ -28,6 +28,7 @@
 #include "jvm.h"
 #include "jlong.h"
 #include "sun_nio_ch_DevPollArrayWrapper.h"
+#include "java_lang_Integer.h"
 #include <sys/poll.h>
 #include <sys/resource.h>
 #include <unistd.h>
@@ -192,7 +193,11 @@
         JNU_ThrowIOExceptionWithLastError(env,
                                           "getrlimit failed");
     }
-    return (jint)rlp.rlim_max;
+    if (rlp.rlim_max < 0 || rlp.rlim_max > java_lang_Integer_MAX_VALUE) {
+        return java_lang_Integer_MAX_VALUE;
+    } else {
+        return (jint)rlp.rlim_max;
+    }
 }
 
 JNIEXPORT void JNICALL
--- a/jdk/src/windows/native/java/io/WinNTFileSystem_md.c	Thu Apr 02 16:51:55 2009 -0700
+++ b/jdk/src/windows/native/java/io/WinNTFileSystem_md.c	Fri Apr 03 10:29:13 2009 -0700
@@ -309,12 +309,13 @@
                     /* No template file */
                     NULL);
     if (h != INVALID_HANDLE_VALUE) {
-        GetFileTime(h, NULL, NULL, &t);
+        if (GetFileTime(h, NULL, NULL, &t)) {
+            modTime.LowPart = (DWORD) t.dwLowDateTime;
+            modTime.HighPart = (LONG) t.dwHighDateTime;
+            rv = modTime.QuadPart / 10000;
+            rv -= 11644473600000;
+        }
         CloseHandle(h);
-        modTime.LowPart = (DWORD) t.dwLowDateTime;
-        modTime.HighPart = (LONG) t.dwHighDateTime;
-        rv = modTime.QuadPart / 10000;
-        rv -= 11644473600000;
     }
     free(pathbuf);
     return rv;
--- a/jdk/src/windows/native/java/lang/java_props_md.c	Thu Apr 02 16:51:55 2009 -0700
+++ b/jdk/src/windows/native/java/lang/java_props_md.c	Fri Apr 03 10:29:13 2009 -0700
@@ -717,6 +717,7 @@
          * Windows Vista family         6               0
          * Windows 2008                 6               0
          *       where ((&ver.wServicePackMinor) + 2) = 1
+         * Windows 7                    6               1
          *
          * This mapping will presumably be augmented as new Windows
          * versions are released.
@@ -773,13 +774,18 @@
                  * and Windows Vista are identical, you must also test
                  * whether the wProductType member is VER_NT_WORKSTATION.
                  * If wProductType is VER_NT_WORKSTATION, the operating
-                 * system is Windows Vista; otherwise, it is Windows
+                 * system is Windows Vista or 7; otherwise, it is Windows
                  * Server 2008."
                  */
-                if (ver.wProductType == VER_NT_WORKSTATION)
-                    sprops.os_name = "Windows Vista";
-                else
+                if (ver.wProductType == VER_NT_WORKSTATION) {
+                    switch (ver.dwMinorVersion) {
+                    case  0: sprops.os_name = "Windows Vista";        break;
+                    case  1: sprops.os_name = "Windows 7";            break;
+                    default: sprops.os_name = "Windows NT (unknown)";
+                    }
+                } else {
                     sprops.os_name = "Windows Server 2008";
+                }
             } else {
                 sprops.os_name = "Windows NT (unknown)";
             }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/Integer/ValueOf.java	Fri Apr 03 10:29:13 2009 -0700
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc.  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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+/**
+ * @test
+ * @bug 6807702
+ * @summary Basic test for Integer.valueOf
+ * @run main ValueOf
+ * @run main/othervm -esa -XX:+AggressiveOpts ValueOf
+ */
+
+public class ValueOf {
+
+    // test Integer.valueOf over this range (inclusive)
+    private static final int TEST_LOW  = -1024;
+    private static final int TEST_HIGH = 24576;
+
+    public static void main(String[] args) {
+        int i = TEST_LOW;
+        while (i <= TEST_HIGH) {
+            // check that valueOf stores i
+            if (Integer.valueOf(i).intValue() != i)
+                throw new RuntimeException();
+
+            // check that the same object is returned for integral values
+            // in the range -128 to 127 (inclusive)
+            if (i >= -128 && i <= 127) {
+                if (Integer.valueOf(i) != Integer.valueOf(i))
+                    throw new RuntimeException();
+            }
+
+            i++;
+        }
+    }
+}
--- a/jdk/test/java/nio/channels/DatagramChannel/NotBound.java	Thu Apr 02 16:51:55 2009 -0700
+++ b/jdk/test/java/nio/channels/DatagramChannel/NotBound.java	Fri Apr 03 10:29:13 2009 -0700
@@ -22,27 +22,110 @@
  */
 
 /* @test
- * @bug 4512723
- * @summary Unit test for datagram-socket-channel adaptors
+ * @bug 4512723 6621689
+ * @summary Test that connect/send/receive with unbound DatagramChannel causes
+ *     the channel's socket to be bound to a local address
  */
 
 import java.net.*;
-import java.nio.*;
-import java.nio.channels.*;
+import java.nio.ByteBuffer;
+import java.nio.channels.DatagramChannel;
+import java.io.IOException;
+
+public class NotBound {
+
+    static void checkBound(DatagramChannel dc) throws IOException {
+        if (dc.getLocalAddress() == null)
+            throw new RuntimeException("Not bound??");
+    }
 
-class NotBound {
-    public static void main(String[] args) throws Exception {
-        test1(false);
-        test1(true);
+    // starts a thread to send a datagram to the given channel once the channel
+    // is bound to a local address
+    static void wakeupWhenBound(final DatagramChannel dc) {
+        Runnable wakeupTask = new Runnable() {
+            public void run() {
+                try {
+                    // poll for local address
+                    InetSocketAddress local;
+                    do {
+                        Thread.sleep(50);
+                        local = (InetSocketAddress)dc.getLocalAddress();
+                    } while (local == null);
+
+                    // send message to channel to wakeup receiver
+                    DatagramChannel sender = DatagramChannel.open();
+                    try {
+                        ByteBuffer bb = ByteBuffer.wrap("hello".getBytes());
+                        InetAddress lh = InetAddress.getLocalHost();
+                        SocketAddress target =
+                            new InetSocketAddress(lh, local.getPort());
+                        sender.send(bb, target);
+                    } finally {
+                        sender.close();
+                    }
+
+                } catch (Exception x) {
+                    x.printStackTrace();
+                }
+            }};
+        new Thread(wakeupTask).start();
     }
 
-    static void test1(boolean blocking) throws Exception {
-        ByteBuffer bb = ByteBuffer.allocateDirect(256);
-        DatagramChannel dc1 = DatagramChannel.open();
-        dc1.configureBlocking(false);
-        SocketAddress isa = dc1.receive(bb);
-        if (isa != null)
-            throw new Exception("Unbound dc returned non-null");
-        dc1.close();
+    public static void main(String[] args) throws IOException {
+        DatagramChannel dc;
+
+        // connect
+        dc = DatagramChannel.open();
+        try {
+            DatagramChannel peer = DatagramChannel.open()
+                .bind(new InetSocketAddress(0));
+            int peerPort = ((InetSocketAddress)(peer.getLocalAddress())).getPort();
+            try {
+                dc.connect(new InetSocketAddress(InetAddress.getLocalHost(), peerPort));
+                checkBound(dc);
+            } finally {
+                peer.close();
+            }
+        } finally {
+            dc.close();
+        }
+
+        // send
+        dc = DatagramChannel.open();
+        try {
+            ByteBuffer bb = ByteBuffer.wrap("ignore this".getBytes());
+            SocketAddress target =
+                new InetSocketAddress(InetAddress.getLocalHost(), 5000);
+            dc.send(bb, target);
+            checkBound(dc);
+        } finally {
+            dc.close();
+        }
+
+        // receive (blocking)
+        dc = DatagramChannel.open();
+        try {
+            ByteBuffer bb = ByteBuffer.allocateDirect(128);
+            wakeupWhenBound(dc);
+            SocketAddress sender = dc.receive(bb);
+            if (sender == null)
+                throw new RuntimeException("Sender should not be null");
+            checkBound(dc);
+        } finally {
+            dc.close();
+        }
+
+        // receive (non-blocking)
+        dc = DatagramChannel.open();
+        try {
+            dc.configureBlocking(false);
+            ByteBuffer bb = ByteBuffer.allocateDirect(128);
+            SocketAddress sender = dc.receive(bb);
+            if (sender != null)
+                throw new RuntimeException("Sender should be null");
+            checkBound(dc);
+        } finally {
+            dc.close();
+        }
     }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/nio/channels/Selector/RegAfterPreClose.java	Fri Apr 03 10:29:13 2009 -0700
@@ -0,0 +1,127 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc.  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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+/* @test
+ * @bug 6693490
+ * @summary Pre-close file descriptor may inadvertently get registered with
+ *     epoll during close
+ */
+
+import java.net.*;
+import java.nio.channels.*;
+import java.util.concurrent.*;
+import java.util.*;
+import java.io.IOException;
+
+public class RegAfterPreClose {
+
+    static volatile boolean done;
+
+    /**
+     * A task that continuously connects to a given address and immediately
+     * closes the connection.
+     */
+    static class Connector implements Runnable {
+        private final SocketAddress sa;
+        Connector(int port) throws IOException {
+            InetAddress lh = InetAddress.getLocalHost();
+            this.sa = new InetSocketAddress(lh, port);
+        }
+        public void run() {
+            while (!done) {
+                try {
+                    SocketChannel.open(sa).close();
+                } catch (IOException x) {
+                    // back-off as probably resource related
+                    try {
+                        Thread.sleep(10);
+                    } catch (InterruptedException  ignore) { }
+                }
+            }
+        }
+    }
+
+    /**
+     * A task that closes a channel.
+     */
+    static class Closer implements Runnable {
+        private final Channel channel;
+        Closer(Channel sc) {
+            this.channel = sc;
+        }
+        public void run() {
+            try {
+                channel.close();
+            } catch (IOException ignore) { }
+        }
+    }
+
+    public static void main(String[] args) throws Exception {
+        // create listener
+        InetSocketAddress isa = new InetSocketAddress(0);
+        ServerSocketChannel ssc = ServerSocketChannel.open();
+        ssc.socket().bind(isa);
+
+        // register with Selector
+        final Selector sel = Selector.open();
+        ssc.configureBlocking(false);
+        SelectionKey key = ssc.register(sel, SelectionKey.OP_ACCEPT);
+
+        ThreadFactory factory = new ThreadFactory() {
+            @Override
+            public Thread newThread(Runnable r) {
+                Thread t = new Thread(r);
+                t.setDaemon(true);
+                return t;
+            }
+        };
+
+        // schedule test to run for 1 minute
+        Executors.newScheduledThreadPool(1, factory).schedule(new Runnable() {
+            public void run() {
+                done = true;
+                sel.wakeup();
+            }}, 1, TimeUnit.MINUTES);
+
+        // create Executor that handles tasks that closes channels
+        // "asynchronously" - this creates the conditions to provoke the bug.
+        Executor executor = Executors.newFixedThreadPool(2, factory);
+
+        // submit task that connects to listener
+        executor.execute(new Connector(ssc.socket().getLocalPort()));
+
+        // loop accepting connections until done (or an IOException is thrown)
+        while (!done) {
+            sel.select();
+            if (key.isAcceptable()) {
+                SocketChannel sc = ssc.accept();
+                if (sc != null) {
+                    sc.configureBlocking(false);
+                    sc.register(sel, SelectionKey.OP_READ);
+                    executor.execute(new Closer(sc));
+                }
+            }
+            sel.selectedKeys().clear();
+        }
+    }
+}
--- a/jdk/test/java/util/Collection/MOAT.java	Thu Apr 02 16:51:55 2009 -0700
+++ b/jdk/test/java/util/Collection/MOAT.java	Fri Apr 03 10:29:13 2009 -0700
@@ -555,6 +555,7 @@
 
             NavigableMap<Integer,Integer> nm =
                 (NavigableMap<Integer,Integer>) m;
+            testNavigableMapRemovers(nm);
             testNavigableMap(nm);
             testNavigableMap(nm.headMap(6, false));
             testNavigableMap(nm.headMap(5, true));
@@ -742,6 +743,97 @@
         equal(it.next(), expected);
     }
 
+    static void equalMaps(Map m1, Map m2) {
+        equal(m1, m2);
+        equal(m2, m1);
+        equal(m1.size(), m2.size());
+        equal(m1.isEmpty(), m2.isEmpty());
+        equal(m1.toString(), m2.toString());
+        check(Arrays.equals(m1.entrySet().toArray(), m2.entrySet().toArray()));
+    }
+
+    @SuppressWarnings({"unchecked", "rawtypes"})
+    static void testNavigableMapRemovers(NavigableMap m)
+    {
+        final Map emptyMap = new HashMap();
+
+        final Map singletonMap = new HashMap();
+        singletonMap.put(1, 2);
+
+        abstract class NavigableMapView {
+            abstract NavigableMap view(NavigableMap m);
+        }
+
+        NavigableMapView[] views = {
+            new NavigableMapView() { NavigableMap view(NavigableMap m) {
+                return m; }},
+            new NavigableMapView() { NavigableMap view(NavigableMap m) {
+                return m.headMap(99, true); }},
+            new NavigableMapView() { NavigableMap view(NavigableMap m) {
+                return m.tailMap(-99, false); }},
+            new NavigableMapView() { NavigableMap view(NavigableMap m) {
+                return m.subMap(-99, true, 99, false); }},
+        };
+
+        abstract class Remover {
+            abstract void remove(NavigableMap m, Object k, Object v);
+        }
+
+        Remover[] removers = {
+            new Remover() { void remove(NavigableMap m, Object k, Object v) {
+                equal(m.remove(k), v); }},
+
+            new Remover() { void remove(NavigableMap m, Object k, Object v) {
+                equal(m.descendingMap().remove(k), v); }},
+            new Remover() { void remove(NavigableMap m, Object k, Object v) {
+                equal(m.descendingMap().headMap(-86, false).remove(k), v); }},
+            new Remover() { void remove(NavigableMap m, Object k, Object v) {
+                equal(m.descendingMap().tailMap(86, true).remove(k), v); }},
+
+            new Remover() { void remove(NavigableMap m, Object k, Object v) {
+                equal(m.headMap(86, true).remove(k), v); }},
+            new Remover() { void remove(NavigableMap m, Object k, Object v) {
+                equal(m.tailMap(-86, true).remove(k), v); }},
+            new Remover() { void remove(NavigableMap m, Object k, Object v) {
+                equal(m.subMap(-86, false, 86, true).remove(k), v); }},
+
+            new Remover() { void remove(NavigableMap m, Object k, Object v) {
+                check(m.keySet().remove(k)); }},
+            new Remover() { void remove(NavigableMap m, Object k, Object v) {
+                check(m.navigableKeySet().remove(k)); }},
+
+            new Remover() { void remove(NavigableMap m, Object k, Object v) {
+                check(m.navigableKeySet().headSet(86, true).remove(k)); }},
+            new Remover() { void remove(NavigableMap m, Object k, Object v) {
+                check(m.navigableKeySet().tailSet(-86, false).remove(k)); }},
+            new Remover() { void remove(NavigableMap m, Object k, Object v) {
+                check(m.navigableKeySet().subSet(-86, true, 86, false)
+                      .remove(k)); }},
+
+            new Remover() { void remove(NavigableMap m, Object k, Object v) {
+                check(m.descendingKeySet().headSet(-86, false).remove(k)); }},
+            new Remover() { void remove(NavigableMap m, Object k, Object v) {
+                check(m.descendingKeySet().tailSet(86, true).remove(k)); }},
+            new Remover() { void remove(NavigableMap m, Object k, Object v) {
+                check(m.descendingKeySet().subSet(86, true, -86, false)
+                      .remove(k)); }},
+        };
+
+        for (NavigableMapView view : views) {
+            for (Remover remover : removers) {
+                try {
+                    m.clear();
+                    equalMaps(m, emptyMap);
+                    equal(m.put(1, 2), null);
+                    equalMaps(m, singletonMap);
+                    NavigableMap v = view.view(m);
+                    remover.remove(v, 1, 2);
+                    equalMaps(m, emptyMap);
+                } catch (Throwable t) { unexpected(t); }
+            }
+        }
+    }
+
     private static void testNavigableMap(NavigableMap<Integer,Integer> m)
     {
         clear(m);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/concurrent/Semaphore/RacingReleases.java	Fri Apr 03 10:29:13 2009 -0700
@@ -0,0 +1,116 @@
+/*
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/licenses/publicdomain
+ */
+
+/*
+ * @test
+ * @bug 6801020 6803402
+ * @summary Try to tickle race conditions in
+ * AbstractQueuedSynchronizer "shared" code
+ */
+
+import java.util.concurrent.Semaphore;
+
+public class RacingReleases {
+
+    /** Increase this for better chance of tickling races */
+    static final int iterations = 1000;
+
+    public static void test(final boolean fair,
+                            final boolean interruptibly)
+        throws Throwable {
+        for (int i = 0; i < iterations; i++) {
+            final Semaphore sem = new Semaphore(0, fair);
+            final Throwable[] badness = new Throwable[1];
+            Runnable blocker = interruptibly ?
+                new Runnable() {
+                    public void run() {
+                        try {
+                            sem.acquire();
+                        } catch (Throwable t) {
+                            badness[0] = t;
+                            throw new Error(t);
+                        }}}
+                :
+                new Runnable() {
+                    public void run() {
+                        try {
+                            sem.acquireUninterruptibly();
+                        } catch (Throwable t) {
+                            badness[0] = t;
+                            throw new Error(t);
+                        }}};
+
+            Thread b1 = new Thread(blocker);
+            Thread b2 = new Thread(blocker);
+            Runnable signaller = new Runnable() {
+                public void run() {
+                    try {
+                        sem.release();
+                    } catch (Throwable t) {
+                        badness[0] = t;
+                        throw new Error(t);
+                    }}};
+            Thread s1 = new Thread(signaller);
+            Thread s2 = new Thread(signaller);
+            Thread[] threads = { b1, b2, s1, s2 };
+            java.util.Collections.shuffle(java.util.Arrays.asList(threads));
+            for (Thread thread : threads)
+                thread.start();
+            for (Thread thread : threads) {
+                thread.join(60 * 1000);
+                if (thread.isAlive())
+                    throw new Error
+                        (String.format
+                         ("Semaphore stuck: permits %d, thread waiting %s%n",
+                          sem.availablePermits(),
+                          sem.hasQueuedThreads() ? "true" : "false"));
+            }
+            if (badness[0] != null)
+                throw new Error(badness[0]);
+            if (sem.availablePermits() != 0)
+              throw new Error(String.valueOf(sem.availablePermits()));
+            if (sem.hasQueuedThreads())
+              throw new Error(String.valueOf(sem.hasQueuedThreads()));
+            if (sem.getQueueLength() != 0)
+              throw new Error(String.valueOf(sem.getQueueLength()));
+            if (sem.isFair() != fair)
+              throw new Error(String.valueOf(sem.isFair()));
+        }
+    }
+
+    public static void main(String[] args) throws Throwable {
+        for (boolean fair : new boolean[] { true, false })
+            for (boolean interruptibly : new boolean[] { true, false })
+                test(fair, interruptibly);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/zip/LargeZip.java	Fri Apr 03 10:29:13 2009 -0700
@@ -0,0 +1,193 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc.  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.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+import java.io.*;
+import java.nio.*;
+import java.util.*;
+import java.util.zip.*;
+
+public class LargeZip {
+    // If true, don't delete large ZIP file created for test.
+    static final boolean debug = System.getProperty("debug") != null;
+
+    //static final int DATA_LEN = 1024 * 1024;
+    static final int DATA_LEN = 80 * 1024;
+    static final int DATA_SIZE = 8;
+
+    static long fileSize = 6L * 1024L * 1024L * 1024L; // 6GB
+
+    static boolean userFile = false;
+
+    static byte[] data;
+    static File largeFile;
+    static String lastEntryName;
+
+    /* args can be empty, in which case check a 3 GB file which is created for
+     * this test (and then deleted).  Or it can be a number, in which case
+     * that designates the size of the file that's created for this test (and
+     * then deleted).  Or it can be the name of a file to use for the test, in
+     * which case it is *not* deleted.  Note that in this last case, the data
+     * comparison might fail.
+     */
+    static void realMain (String[] args) throws Throwable {
+        if (args.length > 0) {
+            try {
+                fileSize = Long.parseLong(args[0]);
+                System.out.println("Testing with file of size " + fileSize);
+            } catch (NumberFormatException ex) {
+                largeFile = new File(args[0]);
+                if (!largeFile.exists()) {
+                    throw new Exception("Specified file " + args[0] + " does not exist");
+                }
+                userFile = true;
+                System.out.println("Testing with user-provided file " + largeFile);
+            }
+        }
+        File testDir = null;
+        if (largeFile == null) {
+            testDir = new File(System.getProperty("test.scratch", "."),
+                                    "LargeZip");
+            if (testDir.exists()) {
+                if (!testDir.delete()) {
+                    throw new Exception("Cannot delete already-existing test directory");
+                }
+            }
+            check(!testDir.exists() && testDir.mkdirs());
+            largeFile = new File(testDir, "largezip.zip");
+            createLargeZip();
+        }
+
+        readLargeZip1();
+        readLargeZip2();
+
+        if (!userFile && !debug) {
+            check(largeFile.delete());
+            check(testDir.delete());
+        }
+    }
+
+    static void createLargeZip() throws Throwable {
+        int iterations = DATA_LEN / DATA_SIZE;
+        ByteBuffer bb = ByteBuffer.allocate(DATA_SIZE);
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        for (int i = 0; i < iterations; i++) {
+            bb.putDouble(0, Math.random());
+            baos.write(bb.array(), 0, DATA_SIZE);
+        }
+        data = baos.toByteArray();
+
+        ZipOutputStream zos = new ZipOutputStream(
+            new BufferedOutputStream(new FileOutputStream(largeFile)));
+        long length = 0;
+        while (length < fileSize) {
+            ZipEntry ze = new ZipEntry("entry-" + length);
+            lastEntryName = ze.getName();
+            zos.putNextEntry(ze);
+            zos.write(data, 0, data.length);
+            zos.closeEntry();
+            length = largeFile.length();
+        }
+        System.out.println("Last entry written is " + lastEntryName);
+        zos.close();
+    }
+
+    static void readLargeZip1() throws Throwable {
+        ZipFile zipFile = new ZipFile(largeFile);
+        ZipEntry entry = null;
+        String entryName = null;
+        int count = 0;
+        Enumeration<? extends ZipEntry> entries = zipFile.entries();
+        while (entries.hasMoreElements()) {
+            entry = entries.nextElement();
+            entryName = entry.getName();
+            count++;
+        }
+        System.out.println("Number of entries read: " + count);
+        System.out.println("Last entry read is " + entryName);
+        check(!entry.isDirectory());
+        if (check(entryName.equals(lastEntryName))) {
+            ByteArrayOutputStream baos = new ByteArrayOutputStream();
+            InputStream is = zipFile.getInputStream(entry);
+            byte buf[] = new byte[4096];
+            int len;
+            while ((len = is.read(buf)) >= 0) {
+                baos.write(buf, 0, len);
+            }
+            baos.close();
+            is.close();
+            check(Arrays.equals(data, baos.toByteArray()));
+        }
+    }
+
+
+    static void readLargeZip2() throws Throwable {
+        ZipInputStream zis = new ZipInputStream(
+            new BufferedInputStream(new FileInputStream(largeFile)));
+        ZipEntry entry = null;
+        String entryName = null;
+        int count = 0;
+        while ((entry = zis.getNextEntry()) != null) {
+            entryName = entry.getName();
+            if (entryName.equals(lastEntryName)) {
+                break;
+            }
+            count++;
+        }
+        System.out.println("Number of entries read: " + count);
+        System.out.println("Last entry read is " + entryName);
+        check(!entry.isDirectory());
+
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+
+        byte buf[] = new byte[4096];
+        int len;
+        while ((len = zis.read(buf)) >= 0) {
+            baos.write(buf, 0, len);
+        }
+        baos.close();
+        check(Arrays.equals(data, baos.toByteArray()));
+        check(zis.getNextEntry() == null);
+        zis.close();
+    }
+
+
+    //--------------------- Infrastructure ---------------------------
+    static volatile int passed = 0, failed = 0;
+    static void pass() {passed++;}
+    static void pass(String msg) {System.out.println(msg); passed++;}
+    static void fail() {failed++; Thread.dumpStack();}
+    static void fail(String msg) {System.out.println(msg); fail();}
+    static void unexpected(Throwable t) {failed++; t.printStackTrace();}
+    static void unexpected(Throwable t, String msg) {
+        System.out.println(msg); failed++; t.printStackTrace();}
+    static boolean check(boolean cond) {if (cond) pass(); else fail(); return cond;}
+    static void equal(Object x, Object y) {
+        if (x == null ? y == null : x.equals(y)) pass();
+        else fail(x + " not equal to " + y);}
+    public static void main(String[] args) throws Throwable {
+        try {realMain(args);} catch (Throwable t) {unexpected(t);}
+        System.out.println("\nPassed = " + passed + " failed = " + failed);
+        if (failed > 0) throw new AssertionError("Some tests failed");}
+}
--- a/jdk/test/java/util/zip/ZipFile/LargeZipFile.java	Thu Apr 02 16:51:55 2009 -0700
+++ b/jdk/test/java/util/zip/ZipFile/LargeZipFile.java	Fri Apr 03 10:29:13 2009 -0700
@@ -158,4 +158,3 @@
         System.out.println("\nPassed = " + passed + " failed = " + failed);
         if (failed > 0) throw new AssertionError("Some tests failed");}
 }
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/security/tools/jarsigner/concise_jarsigner.sh	Fri Apr 03 10:29:13 2009 -0700
@@ -0,0 +1,200 @@
+#
+# Copyright 2009 Sun Microsystems, Inc.  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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+# CA 95054 USA or visit www.sun.com if you need additional information or
+# have any questions.
+#
+
+# @test
+# @bug 6802846
+# @summary jarsigner needs enhanced cert validation(options)
+#
+# @run shell concise_jarsigner.sh
+#
+
+if [ "${TESTJAVA}" = "" ] ; then
+  JAVAC_CMD=`which javac`
+  TESTJAVA=`dirname $JAVAC_CMD`/..
+fi
+
+# set platform-dependent variables
+OS=`uname -s`
+case "$OS" in
+  Windows_* )
+    FS="\\"
+    ;;
+  * )
+    FS="/"
+    ;;
+esac
+
+KT="$TESTJAVA${FS}bin${FS}keytool -storepass changeit -keypass changeit -keystore js.jks"
+JAR=$TESTJAVA${FS}bin${FS}jar
+JARSIGNER=$TESTJAVA${FS}bin${FS}jarsigner
+JAVAC=$TESTJAVA${FS}bin${FS}javac
+
+rm js.jks
+
+echo class A1 {} > A1.java
+echo class A2 {} > A2.java
+echo class A3 {} > A3.java
+echo class A4 {} > A4.java
+echo class A5 {} > A5.java
+echo class A6 {} > A6.java
+
+$JAVAC A1.java A2.java A3.java A4.java A5.java A6.java
+YEAR=`date +%Y`
+
+# ==========================================================
+# First part: output format
+# ==========================================================
+
+$KT -genkeypair -alias a1 -dname CN=a1 -validity 365
+$KT -genkeypair -alias a2 -dname CN=a2 -validity 365
+
+# a.jar includes 8 unsigned, 2 signed by a1 and a2, 2 signed by a3
+$JAR cvf a.jar A1.class A2.class
+$JARSIGNER -keystore js.jks -storepass changeit a.jar a1
+$JAR uvf a.jar A3.class A4.class
+$JARSIGNER -keystore js.jks -storepass changeit a.jar a2
+$JAR uvf a.jar A5.class A6.class
+
+# Verify OK
+$JARSIGNER -verify a.jar
+[ $? = 0 ] || exit $LINENO
+
+# 4(chainNotValidated)+16(hasUnsignedEntry)+32(aliasNotInStore)
+$JARSIGNER -verify a.jar -strict
+[ $? = 52 ] || exit $LINENO
+
+# 16(hasUnsignedEntry)
+$JARSIGNER -verify a.jar -strict -keystore js.jks
+[ $? = 16 ] || exit $LINENO
+
+# 16(hasUnsignedEntry)+32(notSignedByAlias)
+$JARSIGNER -verify a.jar a1 -strict -keystore js.jks
+[ $? = 48 ] || exit $LINENO
+
+# 16(hasUnsignedEntry)
+$JARSIGNER -verify a.jar a1 a2 -strict -keystore js.jks
+[ $? = 16 ] || exit $LINENO
+
+# 12 entries all together
+LINES=`$JARSIGNER -verify a.jar -verbose | grep $YEAR | wc -l`
+[ $LINES = 12 ] || exit $LINENO
+
+# 12 entries all listed
+LINES=`$JARSIGNER -verify a.jar -verbose:grouped | grep $YEAR | wc -l`
+[ $LINES = 12 ] || exit $LINENO
+
+# 3 groups: unrelated, signed, unsigned
+LINES=`$JARSIGNER -verify a.jar -verbose:summary | grep $YEAR | wc -l`
+[ $LINES = 3 ] || exit $LINENO
+
+# 4 groups: unrelated, signed by a1/a2, signed by a2, unsigned
+LINES=`$JARSIGNER -verify a.jar -verbose:summary -certs | grep $YEAR | wc -l`
+[ $LINES = 4 ] || exit $LINENO
+
+# 2*2 for A1/A2, 2 for A3/A4
+LINES=`$JARSIGNER -verify a.jar -verbose -certs | grep "\[certificate" | wc -l`
+[ $LINES = 6 ] || exit $LINENO
+
+# a1,a2 for A1/A2, a2 for A3/A4
+LINES=`$JARSIGNER -verify a.jar -verbose:grouped -certs | grep "\[certificate" | wc -l`
+[ $LINES = 3 ] || exit $LINENO
+
+# a1,a2 for A1/A2, a2 for A3/A4
+LINES=`$JARSIGNER -verify a.jar -verbose:summary -certs | grep "\[certificate" | wc -l`
+[ $LINES = 3 ] || exit $LINENO
+
+# 4 groups
+LINES=`$JARSIGNER -verify a.jar -verbose:summary -certs | grep "more)" | wc -l`
+[ $LINES = 4 ] || exit $LINENO
+
+# ==========================================================
+# Second part: exit code 2, 4, 8
+# 16 and 32 already covered in the first part
+# ==========================================================
+
+$KT -genkeypair -alias expiring -dname CN=expiring -startdate -1m
+$KT -genkeypair -alias expired -dname CN=expired -startdate -10m
+$KT -genkeypair -alias notyetvalid -dname CN=notyetvalid -startdate +1m
+$KT -genkeypair -alias badku -dname CN=badku -ext KU=cRLSign -validity 365
+$KT -genkeypair -alias badeku -dname CN=badeku -ext EKU=sa -validity 365
+$KT -genkeypair -alias goodku -dname CN=goodku -ext KU=dig -validity 365
+$KT -genkeypair -alias goodeku -dname CN=goodeku -ext EKU=codesign -validity 365
+
+# badchain signed by ca, but ca is removed later
+$KT -genkeypair -alias badchain -dname CN=badchain -validity 365
+$KT -genkeypair -alias ca -dname CN=ca -ext bc -validity 365
+$KT -certreq -alias badchain | $KT -gencert -alias ca -validity 365 | \
+        $KT -importcert -alias badchain
+$KT -delete -alias ca
+
+$JARSIGNER -strict -keystore js.jks -storepass changeit a.jar expiring
+[ $? = 2 ] || exit $LINENO
+
+$JARSIGNER -strict -keystore js.jks -storepass changeit a.jar expired
+[ $? = 4 ] || exit $LINENO
+
+$JARSIGNER -strict -keystore js.jks -storepass changeit a.jar notyetvalid
+[ $? = 4 ] || exit $LINENO
+
+$JARSIGNER -strict -keystore js.jks -storepass changeit a.jar badku
+[ $? = 8 ] || exit $LINENO
+
+$JARSIGNER -strict -keystore js.jks -storepass changeit a.jar badeku
+[ $? = 8 ] || exit $LINENO
+
+$JARSIGNER -strict -keystore js.jks -storepass changeit a.jar goodku
+[ $? = 0 ] || exit $LINENO
+
+$JARSIGNER -strict -keystore js.jks -storepass changeit a.jar goodeku
+[ $? = 0 ] || exit $LINENO
+
+$JARSIGNER -strict -keystore js.jks -storepass changeit a.jar badchain
+[ $? = 4 ] || exit $LINENO
+
+$JARSIGNER -verify a.jar
+[ $? = 0 ] || exit $LINENO
+
+# ==========================================================
+# Third part: -certchain test
+# ==========================================================
+
+# altchain signed by ca2, but ca2 is removed later
+$KT -genkeypair -alias altchain -dname CN=altchain -validity 365
+$KT -genkeypair -alias ca2 -dname CN=ca2 -ext bc -validity 365
+$KT -certreq -alias altchain | $KT -gencert -alias ca2 -validity 365 -rfc > certchain
+$KT -exportcert -alias ca2 -rfc >> certchain
+$KT -delete -alias ca2
+
+# Now altchain is still self-signed
+$JARSIGNER -strict -keystore js.jks -storepass changeit a.jar altchain
+[ $? = 0 ] || exit $LINENO
+
+# If -certchain is used, then it's bad
+$JARSIGNER -strict -keystore js.jks -storepass changeit -certchain certchain a.jar altchain
+[ $? = 4 ] || exit $LINENO
+
+$JARSIGNER -verify a.jar
+[ $? = 0 ] || exit $LINENO
+
+echo OK
+exit 0
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/security/tools/keytool/selfissued.sh	Fri Apr 03 10:29:13 2009 -0700
@@ -0,0 +1,69 @@
+#
+# Copyright 2009 Sun Microsystems, Inc.  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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+# CA 95054 USA or visit www.sun.com if you need additional information or
+# have any questions.
+#
+
+# @test
+# @bug 6825352
+# @summary support self-issued certificate in keytool
+#
+# @run shell selfissued.sh
+#
+
+if [ "${TESTJAVA}" = "" ] ; then
+  JAVAC_CMD=`which javac`
+  TESTJAVA=`dirname $JAVAC_CMD`/..
+fi
+
+# set platform-dependent variables
+OS=`uname -s`
+case "$OS" in
+  Windows_* )
+    FS="\\"
+    ;;
+  * )
+    FS="/"
+    ;;
+esac
+
+KS=selfsigned.jks
+KT="$TESTJAVA${FS}bin${FS}keytool -storepass changeit -keypass changeit -keystore $KS"
+
+rm $KS
+
+$KT -alias ca -dname CN=CA -genkeypair
+$KT -alias me -dname CN=CA -genkeypair
+$KT -alias e1 -dname CN=E1 -genkeypair
+$KT -alias e2 -dname CN=E2 -genkeypair
+
+# me signed by ca, self-issued
+$KT -alias me -certreq | $KT -alias ca -gencert | $KT -alias me -importcert
+
+# Import e1 signed by me, should add me and ca
+$KT -alias e1 -certreq | $KT -alias me -gencert | $KT -alias e1 -importcert
+$KT -alias e1 -list -v | grep '\[3\]' || { echo Bad E1; exit 1; }
+
+# Import (e2 signed by me,ca,me), should reorder to (e2,me,ca)
+( $KT -alias e2 -certreq | $KT -alias me -gencert; $KT -exportcert -alias ca; $KT -exportcert -alias me ) | $KT -alias e2 -importcert
+$KT -alias e2 -list -v | grep '\[3\]' || { echo Bad E2; exit 1; }
+
+echo Good
+