OpenJDK / amber / amber
changeset 4091:82c116e2e288
Merge
author | xlu |
---|---|
date | Wed, 28 Oct 2009 10:37:17 -0700 |
parents | 3dd0ee516f2d 946518568340 |
children | af3776a63c5e |
files | jdk/src/share/classes/sun/security/provider/certpath/LDAPCertStore.java |
diffstat | 69 files changed, 3852 insertions(+), 1305 deletions(-) [+] |
line wrap: on
line diff
--- a/.hgtags Fri Oct 23 18:44:33 2009 -0700 +++ b/.hgtags Wed Oct 28 10:37:17 2009 -0700 @@ -48,3 +48,4 @@ c5d39b6be65cba0effb5f466ea48fe43764d0e0c jdk7-b71 df4bcd06e1d0ab306efa5a44f24a409dc0c0c742 jdk7-b72 ce74bd35ce948d629a356e168797f44b593b1578 jdk7-b73 +4e7661eaa211e186674f6cbefec4aef1144ac2a0 jdk7-b74
--- a/.hgtags-top-repo Fri Oct 23 18:44:33 2009 -0700 +++ b/.hgtags-top-repo Wed Oct 28 10:37:17 2009 -0700 @@ -48,3 +48,4 @@ 4c36e9853dda27bdac5ef4839a610509fbe31d34 jdk7-b71 0d7e03b426df27c21dcc44ffb9178eacd1b04f10 jdk7-b72 3ac6dcf7823205546fbbc3d4ea59f37358d0b0d4 jdk7-b73 +2c88089b6e1c053597418099a14232182c387edc jdk7-b74
--- a/corba/.hgtags Fri Oct 23 18:44:33 2009 -0700 +++ b/corba/.hgtags Wed Oct 28 10:37:17 2009 -0700 @@ -48,3 +48,4 @@ 3f1ef7f899ea2aec189c4fb67e5c8fa374437c50 jdk7-b71 c793a31209263fbb867c23c752599d85c21abb73 jdk7-b72 b751c528c55560cf2adeaeef24b39ca1f4d1cbf7 jdk7-b73 +5d0cf59a3203b9f57aceebc33ae656b884987955 jdk7-b74
--- a/hotspot/.hgtags Fri Oct 23 18:44:33 2009 -0700 +++ b/hotspot/.hgtags Wed Oct 28 10:37:17 2009 -0700 @@ -48,3 +48,4 @@ 50a95aa4a247f0cbbf66df285a8b1d78ffb153d9 jdk7-b71 a94714c550658fd6741793ef036cb9625dc2ab1a jdk7-b72 faf94d94786b621f8e13cbcc941ca69c6d967c3f jdk7-b73 +f4b900403d6e4b0af51447bd13bbe23fe3a1dac7 jdk7-b74
--- a/hotspot/make/hotspot_version Fri Oct 23 18:44:33 2009 -0700 +++ b/hotspot/make/hotspot_version Wed Oct 28 10:37:17 2009 -0700 @@ -35,7 +35,7 @@ HS_MAJOR_VER=17 HS_MINOR_VER=0 -HS_BUILD_NUMBER=03 +HS_BUILD_NUMBER=04 JDK_MAJOR_VER=1 JDK_MINOR_VER=7
--- a/jaxp/.hgtags Fri Oct 23 18:44:33 2009 -0700 +++ b/jaxp/.hgtags Wed Oct 28 10:37:17 2009 -0700 @@ -48,3 +48,4 @@ ff94d8ce0daded647bb326630e643d010357afce jdk7-b71 37c805b6156fd492c12301688b54a6bcca39e729 jdk7-b72 feb05980f9f2964e6bc2b3a8532f9b3054c2289b jdk7-b73 +ea7b88c676dd8b269bc858a4a17c14dc96c8aed1 jdk7-b74
--- a/jaxws/.hgtags Fri Oct 23 18:44:33 2009 -0700 +++ b/jaxws/.hgtags Wed Oct 28 10:37:17 2009 -0700 @@ -48,3 +48,4 @@ 03314cf56a7212bbb6c186dbc9f15aca988a48ec jdk7-b71 4c990aa99bc037fd81dd1b1b269690e9bea8a0b4 jdk7-b72 558985e26fe16f5a6ebb2edb9180a42e1c8e8202 jdk7-b73 +f4466e1b608088c90e11beaa4b600f102608c6a1 jdk7-b74
--- a/jdk/.hgtags Fri Oct 23 18:44:33 2009 -0700 +++ b/jdk/.hgtags Wed Oct 28 10:37:17 2009 -0700 @@ -48,3 +48,4 @@ b3f3240135f0c10b9f2481c174b81b7fcf0daa60 jdk7-b71 460639b036f327282832a4fe52b7aa45688afd50 jdk7-b72 f708138c9aca4b389872838fe6773872fce3609e jdk7-b73 +eacb36e30327e7ae33baa068e82ddccbd91eaae2 jdk7-b74
--- a/jdk/make/common/shared/Defs-java.gmk Fri Oct 23 18:44:33 2009 -0700 +++ b/jdk/make/common/shared/Defs-java.gmk Wed Oct 28 10:37:17 2009 -0700 @@ -165,11 +165,6 @@ JAVADOC_CMD = $(JAVA_TOOLS_DIR)/javadoc $(JAVA_TOOLS_FLAGS:%=-J%) endif -#always use the bootstrap javah until bug-ID 6889255 is fixed. These -#five lines should be removed as part of that fix: -JAVAH_CMD = $(JAVA_TOOLS_DIR)/javah \ - $(JAVAHFLAGS) - # Override of what javac to use (see deploy workspace) ifdef JAVAC JAVAC_CMD = $(JAVAC)
--- a/jdk/make/java/java/FILES_java.gmk Fri Oct 23 18:44:33 2009 -0700 +++ b/jdk/make/java/java/FILES_java.gmk Wed Oct 28 10:37:17 2009 -0700 @@ -258,6 +258,7 @@ java/util/ServiceConfigurationError.java \ java/util/Timer.java \ java/util/TimerTask.java \ + java/util/Objects.java \ java/util/UUID.java \ java/util/concurrent/AbstractExecutorService.java \ java/util/concurrent/ArrayBlockingQueue.java \
--- a/jdk/make/java/redist/Makefile Fri Oct 23 18:44:33 2009 -0700 +++ b/jdk/make/java/redist/Makefile Wed Oct 28 10:37:17 2009 -0700 @@ -211,7 +211,7 @@ $(LIB_LOCATION)/$(KERNEL_LOCATION)/$(JVM_NAME): $(HOTSPOT_KERNEL_PATH)/$(JVM_NAME) $(install-file) -$(LIB_LOCATION)/$(LIBJSIG_NAME): $(HOTSPOT_SERVER_PATH)/$(LIBJSIG_NAME) +$(LIB_LOCATION)/$(LIBJSIG_NAME): $(HOTSPOT_IMPORT_PATH)/$(ARCH_VM_SUBDIR)/$(LIBJSIG_NAME) $(install-import-file) $(LIB_LOCATION)/$(CLIENT_LOCATION)/$(LIBJSIG_NAME) \
--- a/jdk/src/share/classes/com/sun/naming/internal/ResourceManager.java Fri Oct 23 18:44:33 2009 -0700 +++ b/jdk/src/share/classes/com/sun/naming/internal/ResourceManager.java Wed Oct 28 10:37:17 2009 -0700 @@ -25,11 +25,12 @@ package com.sun.naming.internal; -import java.applet.Applet; import java.io.InputStream; import java.io.IOException; import java.net.URL; import java.lang.ref.WeakReference; +import java.lang.reflect.Method; +import java.lang.reflect.InvocationTargetException; import java.util.Enumeration; import java.util.HashMap; import java.util.Hashtable; @@ -112,6 +113,52 @@ private static final WeakHashMap urlFactoryCache = new WeakHashMap(11); private static final WeakReference NO_FACTORY = new WeakReference(null); + /** + * A class to allow JNDI properties be specified as applet parameters + * without creating a static dependency on java.applet. + */ + private static class AppletParameter { + private static final Class<?> clazz = getClass("java.applet.Applet"); + private static final Method getMethod = + getMethod(clazz, "getParameter", String.class); + private static Class<?> getClass(String name) { + try { + return Class.forName(name, true, null); + } catch (ClassNotFoundException e) { + return null; + } + } + private static Method getMethod(Class<?> clazz, + String name, + Class<?>... paramTypes) + { + if (clazz != null) { + try { + return clazz.getMethod(name, paramTypes); + } catch (NoSuchMethodException e) { + throw new AssertionError(e); + } + } else { + return null; + } + } + + /** + * Returns the value of the applet's named parameter. + */ + static Object get(Object applet, String name) { + // if clazz is null then applet cannot be an Applet. + if (clazz == null || !clazz.isInstance(applet)) + throw new ClassCastException(applet.getClass().getName()); + try { + return getMethod.invoke(applet, name); + } catch (InvocationTargetException e) { + throw new AssertionError(e); + } catch (IllegalAccessException iae) { + throw new AssertionError(iae); + } + } + } // There should be no instances of this class. private ResourceManager() { @@ -143,7 +190,7 @@ if (env == null) { env = new Hashtable(11); } - Applet applet = (Applet)env.get(Context.APPLET); + Object applet = env.get(Context.APPLET); // Merge property values from env param, applet params, and system // properties. The first value wins: there's no concatenation of @@ -157,7 +204,7 @@ Object val = env.get(props[i]); if (val == null) { if (applet != null) { - val = applet.getParameter(props[i]); + val = AppletParameter.get(applet, props[i]); } if (val == null) { // Read system property.
--- a/jdk/src/share/classes/java/io/FilePermission.java Fri Oct 23 18:44:33 2009 -0700 +++ b/jdk/src/share/classes/java/io/FilePermission.java Wed Oct 28 10:37:17 2009 -0700 @@ -209,7 +209,17 @@ cpath = AccessController.doPrivileged(new PrivilegedAction<String>() { public String run() { try { - return sun.security.provider.PolicyFile.canonPath(cpath); + String path = cpath; + if (cpath.endsWith("*")) { + // call getCanonicalPath with a path with wildcard character + // replaced to avoid calling it with paths that are + // intended to match all entries in a directory + path = path.substring(0, path.length()-1) + "-"; + path = new File(path).getCanonicalPath(); + return path.substring(0, path.length()-1) + "*"; + } else { + return new File(path).getCanonicalPath(); + } } catch (IOException ioe) { return cpath; }
--- a/jdk/src/share/classes/java/lang/Enum.java Fri Oct 23 18:44:33 2009 -0700 +++ b/jdk/src/share/classes/java/lang/Enum.java Wed Oct 28 10:37:17 2009 -0700 @@ -40,10 +40,17 @@ * Edition</i>, <a * href="http://java.sun.com/docs/books/jls/third_edition/html/classes.html#8.9">§8.9</a>. * + * <p> Note that when using an enumeration type as the type of a set + * or as the type of the keys in a map, specialized and efficient + * {@linkplain java.util.EnumSet set} and {@linkplain + * java.util.EnumMap map} implementations are available. + * * @param <E> The enum type subclass * @author Josh Bloch * @author Neal Gafter * @see Class#getEnumConstants() + * @see java.util.EnumSet + * @see java.util.EnumMap * @since 1.5 */ public abstract class Enum<E extends Enum<E>>
--- a/jdk/src/share/classes/java/lang/reflect/AccessibleObject.java Fri Oct 23 18:44:33 2009 -0700 +++ b/jdk/src/share/classes/java/lang/reflect/AccessibleObject.java Wed Oct 28 10:37:17 2009 -0700 @@ -44,6 +44,8 @@ * as Java Object Serialization or other persistence mechanisms, to * manipulate objects in a manner that would normally be prohibited. * + * <p>By default, a reflected object is <em>not</em> accessible. + * * @see Field * @see Method * @see Constructor
--- a/jdk/src/share/classes/java/nio/file/Path.java Fri Oct 23 18:44:33 2009 -0700 +++ b/jdk/src/share/classes/java/nio/file/Path.java Wed Oct 28 10:37:17 2009 -0700 @@ -412,7 +412,7 @@ * dependent if {@code "a/b/../x"} would locate the same file as {@code "/a/x"}. * * @param other - * the resulting path + * the path to relativize against this path * * @return the resulting relative path, or {@code null} if both paths are * equal @@ -1615,23 +1615,23 @@ * Tests if the file referenced by this object is the same file referenced * by another object. * - * <p> If this {@code FileRef} and the given {@code FileRef} are {@link + * <p> If this {@code Path} and the given {@code Path} are {@link * #equals(Object) equal} then this method returns {@code true} without checking - * if the file exists. If the {@code FileRef} and the given {@code FileRef} - * are associated with different providers, or the given {@code FileRef} is + * if the file exists. If the {@code Path} and the given {@code Path} + * are associated with different providers, or the given {@code Path} is * {@code null} then this method returns {@code false}. Otherwise, this method - * checks if both {@code FileRefs} locate the same file, and depending on the + * checks if both {@code Paths} locate the same file, and depending on the * implementation, may require to open or access both files. * * <p> If the file system and files remain static, then this method implements - * an equivalence relation for non-null {@code FileRefs}. + * an equivalence relation for non-null {@code Paths}. * <ul> - * <li>It is <i>reflexive</i>: for a non-null {@code FileRef} {@code f}, + * <li>It is <i>reflexive</i>: for a non-null {@code Path} {@code f}, * {@code f.isSameFile(f)} should return {@code true}. - * <li>It is <i>symmetric</i>: for two non-null {@code FileRefs} + * <li>It is <i>symmetric</i>: for two non-null {@code Path} * {@code f} and {@code g}, {@code f.isSameFile(g)} will equal * {@code g.isSameFile(f)}. - * <li>It is <i>transitive</i>: for three {@code FileRefs} + * <li>It is <i>transitive</i>: for three {@code Paths} * {@code f}, {@code g}, and {@code h}, if {@code f.isSameFile(g)} returns * {@code true} and {@code g.isSameFile(h)} returns {@code true}, then * {@code f.isSameFile(h)} will return return {@code true}.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/share/classes/java/util/Objects.java Wed Oct 28 10:37:17 2009 -0700 @@ -0,0 +1,156 @@ +/* + * 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. + */ + +package java.util; + +/** + * This class consists of {@code static} utility methods for operating + * on objects. These utilities include {@code null}-safe or {@code + * null}-tolerant methods for computing the hash code of an object, + * returning a string for an object, and comparing two objects. + * + * @since 1.7 + */ +public class Objects { + private Objects() { + throw new AssertionError("No java.util.Objects instances for you!"); + } + + /** + * Returns {@code true} if the arguments are equal to each other + * and {@code false} otherwise. + * Consequently, if both arguments are {@code null}, {@code true} + * is returned and if exactly one argument is {@code null}, {@code + * false} is returned. Otherwise, equality is determined by using + * the {@link Object#equals equals} method of the first + * argument. + * + * @param a an object + * @param b an object to be compared with {@code a} for equality + * @return {@code true} if the arguments are equal to each other + * and {@code false} otherwise + * @see Object#equals(Object) + */ + public static boolean equals(Object a, Object b) { + return (a == b) || (a != null && a.equals(b)); + } + + /** + * Returns the hash code of a non-{@code null} argument and 0 for + * a {@code null} argument. + * + * @param o an object + * @return the hash code of a non-{@code null} argument and 0 for + * a {@code null} argument + * @see Object#hashCode + */ + public static int hashCode(Object o) { + return o != null ? o.hashCode() : 0; + } + + /** + * Returns the result of calling {@code toString} for a non-{@code + * null} argument and {@code "null"} for a {@code null} argument. + * + * @param o an object + * @return the result of calling {@code toString} for a non-{@code + * null} argument and {@code "null"} for a {@code null} argument + * @see Object#toString + * @see String#valueOf(Object) + */ + public static String toString(Object o) { + return String.valueOf(o); + } + + /** + * Returns 0 if the arguments are identical and {@code + * c.compare(a, b)} otherwise. + * Consequently, if both arguments are {@code null} 0 + * is returned. + * + * <p>Note that if one of the arguments is {@code null}, a {@code + * NullPointerException} may or may not be thrown depending on + * what ordering policy, if any, the {@link Comparator Comparator} + * chooses to have for {@code null} values. + * + * @param <T> the type of the objects being compared + * @param a an object + * @param b an object to be compared with {@code a} + * @param c the {@code Comparator} to compare the first two arguments + * @return 0 if the arguments are identical and {@code + * c.compare(a, b)} otherwise. + * @see Comparable + * @see Comparator + */ + public static <T> int compare(T a, T b, Comparator<? super T> c) { + return (a == b) ? 0 : c.compare(a, b); + } + + /** + * Checks that the specified object reference is not {@code null}. This + * method is designed primarily for doing parameter validation in methods + * and constructors, as demonstrated below: + * <blockquote><pre> + * public Foo(Bar bar) { + * this.bar = Objects.nonNull(bar); + * } + * </pre></blockquote> + * + * @param obj the object reference to check for nullity + * @param <T> the type of the reference + * @return {@code obj} if not {@code null} + * @throws NullPointerException if {@code obj} is {@code null} + */ + public static <T> T nonNull(T obj) { + if (obj == null) + throw new NullPointerException(); + return obj; + } + + /** + * Checks that the specified object reference is not {@code null} and + * throws a customized {@link NullPointerException} if it is. This method + * is designed primarily for doing parameter validation in methods and + * constructors with multiple parameters, as demonstrated below: + * <blockquote><pre> + * public Foo(Bar bar, Baz baz) { + * this.bar = Objects.nonNull(bar, "bar must not be null"); + * this.baz = Objects.nonNull(baz, "baz must not be null"); + * } + * </pre></blockquote> + * + * @param obj the object reference to check for nullity + * @param message detail message to be used in the event that a {@code + * NullPointerException} is thrown + * @param <T> the type of the reference + * @return {@code obj} if not {@code null} + * @throws NullPointerException if {@code obj} is {@code null} + */ + public static <T> T nonNull(T obj, String message) { + if (obj == null) + throw new NullPointerException(message); + return obj; + } +}
--- a/jdk/src/share/classes/java/util/jar/JarVerifier.java Fri Oct 23 18:44:33 2009 -0700 +++ b/jdk/src/share/classes/java/util/jar/JarVerifier.java Wed Oct 28 10:37:17 2009 -0700 @@ -293,10 +293,8 @@ } sfv.process(sigFileSigners); - } catch (sun.security.pkcs.ParsingException pe) { - if (debug != null) debug.println("processEntry caught: "+pe); - // ignore and treat as unsigned } catch (IOException ioe) { + // e.g. sun.security.pkcs.ParsingException if (debug != null) debug.println("processEntry caught: "+ioe); // ignore and treat as unsigned } catch (SignatureException se) {
--- a/jdk/src/share/classes/sun/misc/FloatingDecimal.java Fri Oct 23 18:44:33 2009 -0700 +++ b/jdk/src/share/classes/sun/misc/FloatingDecimal.java Wed Oct 28 10:37:17 2009 -0700 @@ -730,7 +730,7 @@ * Thus we will need more than one digit if we're using * E-form */ - if ( decExp <= -3 || decExp >= 8 ){ + if ( decExp < -3 || decExp >= 8 ){ high = low = false; } while( ! low && ! high ){ @@ -783,7 +783,7 @@ * Thus we will need more than one digit if we're using * E-form */ - if ( decExp <= -3 || decExp >= 8 ){ + if ( decExp < -3 || decExp >= 8 ){ high = low = false; } while( ! low && ! high ){ @@ -847,7 +847,7 @@ * Thus we will need more than one digit if we're using * E-form */ - if ( decExp <= -3 || decExp >= 8 ){ + if ( decExp < -3 || decExp >= 8 ){ high = low = false; } while( ! low && ! high ){
--- a/jdk/src/share/classes/sun/net/httpserver/ExchangeImpl.java Fri Oct 23 18:44:33 2009 -0700 +++ b/jdk/src/share/classes/sun/net/httpserver/ExchangeImpl.java Wed Oct 28 10:37:17 2009 -0700 @@ -31,6 +31,7 @@ import java.net.*; import javax.net.ssl.*; import java.util.*; +import java.util.logging.Logger; import java.text.*; import sun.net.www.MessageHeader; import com.sun.net.httpserver.*; @@ -204,6 +205,21 @@ tmpout.write (bytes(statusLine, 0), 0, statusLine.length()); boolean noContentToSend = false; // assume there is content rspHdrs.set ("Date", df.format (new Date())); + + /* check for response type that is not allowed to send a body */ + + if ((rCode>=100 && rCode <200) /* informational */ + ||(rCode == 204) /* no content */ + ||(rCode == 304)) /* not modified */ + { + if (contentLen != -1) { + Logger logger = server.getLogger(); + String msg = "sendResponseHeaders: rCode = "+ rCode + + ": forcing contentLen = -1"; + logger.warning (msg); + } + contentLen = -1; + } if (contentLen == 0) { if (http10) { o.setWrappedStream (new UndefLengthOutputStream (this, ros));
--- a/jdk/src/share/classes/sun/net/www/protocol/http/HttpURLConnection.java Fri Oct 23 18:44:33 2009 -0700 +++ b/jdk/src/share/classes/sun/net/www/protocol/http/HttpURLConnection.java Wed Oct 28 10:37:17 2009 -0700 @@ -1180,6 +1180,10 @@ inputStream = http.getInputStream(); respCode = getResponseCode(); + if (respCode == -1) { + disconnectInternal(); + throw new IOException ("Invalid Http response"); + } if (respCode == HTTP_PROXY_AUTH) { if (streaming()) { disconnectInternal();
--- a/jdk/src/share/classes/sun/security/provider/PolicyFile.java Fri Oct 23 18:44:33 2009 -0700 +++ b/jdk/src/share/classes/sun/security/provider/PolicyFile.java Wed Oct 28 10:37:17 2009 -0700 @@ -1832,8 +1832,9 @@ return canonCs; } - // public for java.io.FilePermission - public static String canonPath(String path) throws IOException { + // Wrapper to return a canonical path that avoids calling getCanonicalPath() + // with paths that are intended to match all entries in the directory + private static String canonPath(String path) throws IOException { if (path.endsWith("*")) { path = path.substring(0, path.length()-1) + "-"; path = new File(path).getCanonicalPath();
--- a/jdk/src/share/classes/sun/security/provider/SunEntries.java Fri Oct 23 18:44:33 2009 -0700 +++ b/jdk/src/share/classes/sun/security/provider/SunEntries.java Wed Oct 28 10:37:17 2009 -0700 @@ -210,7 +210,7 @@ * CertStores */ map.put("CertStore.LDAP", - "sun.security.provider.certpath.LDAPCertStore"); + "sun.security.provider.certpath.ldap.LDAPCertStore"); map.put("CertStore.LDAP LDAPSchema", "RFC2587"); map.put("CertStore.Collection", "sun.security.provider.certpath.CollectionCertStore");
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/share/classes/sun/security/provider/certpath/CertStoreHelper.java Wed Oct 28 10:37:17 2009 -0700 @@ -0,0 +1,68 @@ +/* + * 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. + */ + +package sun.security.provider.certpath; + +import java.net.URI; +import java.util.Collection; +import java.security.NoSuchAlgorithmException; +import java.security.InvalidAlgorithmParameterException; +import java.security.cert.CertStore; +import java.security.cert.X509CertSelector; +import java.security.cert.X509CRLSelector; +import javax.security.auth.x500.X500Principal; +import java.io.IOException; + +/** + * Helper used by URICertStore when delegating to another CertStore to + * fetch certs and CRLs. + */ + +public interface CertStoreHelper { + + /** + * Returns a CertStore using the given URI as parameters. + */ + CertStore getCertStore(URI uri) + throws NoSuchAlgorithmException, InvalidAlgorithmParameterException; + + /** + * Wraps an existing X509CertSelector when needing to avoid DN matching + * issues. + */ + X509CertSelector wrap(X509CertSelector selector, + X500Principal certSubject, + String dn) + throws IOException; + + /** + * Wraps an existing X509CRLSelector when needing to avoid DN matching + * issues. + */ + X509CRLSelector wrap(X509CRLSelector selector, + Collection<X500Principal> certIssuers, + String dn) + throws IOException; +}
--- a/jdk/src/share/classes/sun/security/provider/certpath/LDAPCertStore.java Fri Oct 23 18:44:33 2009 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1065 +0,0 @@ -/* - * Copyright 2000-2006 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 sun.security.provider.certpath; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.math.BigInteger; -import java.net.URI; -import java.util.*; -import javax.naming.Context; -import javax.naming.NamingEnumeration; -import javax.naming.NamingException; -import javax.naming.NameNotFoundException; -import javax.naming.directory.Attribute; -import javax.naming.directory.Attributes; -import javax.naming.directory.BasicAttributes; -import javax.naming.directory.DirContext; -import javax.naming.directory.InitialDirContext; - -import java.security.*; -import java.security.cert.Certificate; -import java.security.cert.*; -import javax.security.auth.x500.X500Principal; - -import sun.misc.HexDumpEncoder; -import sun.security.util.Cache; -import sun.security.util.Debug; -import sun.security.x509.X500Name; -import sun.security.action.GetPropertyAction; - -/** - * A <code>CertStore</code> that retrieves <code>Certificates</code> and - * <code>CRL</code>s from an LDAP directory, using the PKIX LDAP V2 Schema - * (RFC 2587): - * <a href="http://www.ietf.org/rfc/rfc2587.txt"> - * http://www.ietf.org/rfc/rfc2587.txt</a>. - * <p> - * Before calling the {@link #engineGetCertificates engineGetCertificates} or - * {@link #engineGetCRLs engineGetCRLs} methods, the - * {@link #LDAPCertStore(CertStoreParameters) - * LDAPCertStore(CertStoreParameters)} constructor is called to create the - * <code>CertStore</code> and establish the DNS name and port of the LDAP - * server from which <code>Certificate</code>s and <code>CRL</code>s will be - * retrieved. - * <p> - * <b>Concurrent Access</b> - * <p> - * As described in the javadoc for <code>CertStoreSpi</code>, the - * <code>engineGetCertificates</code> and <code>engineGetCRLs</code> methods - * must be thread-safe. That is, multiple threads may concurrently - * invoke these methods on a single <code>LDAPCertStore</code> object - * (or more than one) with no ill effects. This allows a - * <code>CertPathBuilder</code> to search for a CRL while simultaneously - * searching for further certificates, for instance. - * <p> - * This is achieved by adding the <code>synchronized</code> keyword to the - * <code>engineGetCertificates</code> and <code>engineGetCRLs</code> methods. - * <p> - * This classes uses caching and requests multiple attributes at once to - * minimize LDAP round trips. The cache is associated with the CertStore - * instance. It uses soft references to hold the values to minimize impact - * on footprint and currently has a maximum size of 750 attributes and a - * 30 second default lifetime. - * <p> - * We always request CA certificates, cross certificate pairs, and ARLs in - * a single LDAP request when any one of them is needed. The reason is that - * we typically need all of them anyway and requesting them in one go can - * reduce the number of requests to a third. Even if we don't need them, - * these attributes are typically small enough not to cause a noticeable - * overhead. In addition, when the prefetchCRLs flag is true, we also request - * the full CRLs. It is currently false initially but set to true once any - * request for an ARL to the server returns an null value. The reason is - * that CRLs could be rather large but are rarely used. This implementation - * should improve performance in most cases. - * - * @see java.security.cert.CertStore - * - * @since 1.4 - * @author Steve Hanna - * @author Andreas Sterbenz - */ -public class LDAPCertStore extends CertStoreSpi { - - private static final Debug debug = Debug.getInstance("certpath"); - - private final static boolean DEBUG = false; - - /** - * LDAP attribute identifiers. - */ - private static final String USER_CERT = "userCertificate;binary"; - private static final String CA_CERT = "cACertificate;binary"; - private static final String CROSS_CERT = "crossCertificatePair;binary"; - private static final String CRL = "certificateRevocationList;binary"; - private static final String ARL = "authorityRevocationList;binary"; - private static final String DELTA_CRL = "deltaRevocationList;binary"; - - // Constants for various empty values - private final static String[] STRING0 = new String[0]; - - private final static byte[][] BB0 = new byte[0][]; - - private final static Attributes EMPTY_ATTRIBUTES = new BasicAttributes(); - - // cache related constants - private final static int DEFAULT_CACHE_SIZE = 750; - private final static int DEFAULT_CACHE_LIFETIME = 30; - - private final static int LIFETIME; - - private final static String PROP_LIFETIME = - "sun.security.certpath.ldap.cache.lifetime"; - - static { - String s = AccessController.doPrivileged( - new GetPropertyAction(PROP_LIFETIME)); - if (s != null) { - LIFETIME = Integer.parseInt(s); // throws NumberFormatException - } else { - LIFETIME = DEFAULT_CACHE_LIFETIME; - } - } - - /** - * The CertificateFactory used to decode certificates from - * their binary stored form. - */ - private CertificateFactory cf; - /** - * The JNDI directory context. - */ - private DirContext ctx; - - /** - * Flag indicating whether we should prefetch CRLs. - */ - private boolean prefetchCRLs = false; - - private final Cache valueCache; - - private int cacheHits = 0; - private int cacheMisses = 0; - private int requests = 0; - - /** - * Creates a <code>CertStore</code> with the specified parameters. - * For this class, the parameters object must be an instance of - * <code>LDAPCertStoreParameters</code>. - * - * @param params the algorithm parameters - * @exception InvalidAlgorithmParameterException if params is not an - * instance of <code>LDAPCertStoreParameters</code> - */ - public LDAPCertStore(CertStoreParameters params) - throws InvalidAlgorithmParameterException { - super(params); - if (!(params instanceof LDAPCertStoreParameters)) - throw new InvalidAlgorithmParameterException( - "parameters must be LDAPCertStoreParameters"); - - LDAPCertStoreParameters lparams = (LDAPCertStoreParameters) params; - - // Create InitialDirContext needed to communicate with the server - createInitialDirContext(lparams.getServerName(), lparams.getPort()); - - // Create CertificateFactory for use later on - try { - cf = CertificateFactory.getInstance("X.509"); - } catch (CertificateException e) { - throw new InvalidAlgorithmParameterException( - "unable to create CertificateFactory for X.509"); - } - if (LIFETIME == 0) { - valueCache = Cache.newNullCache(); - } else if (LIFETIME < 0) { - valueCache = Cache.newSoftMemoryCache(DEFAULT_CACHE_SIZE); - } else { - valueCache = Cache.newSoftMemoryCache(DEFAULT_CACHE_SIZE, LIFETIME); - } - } - - /** - * Returns an LDAP CertStore. This method consults a cache of - * CertStores (shared per JVM) using the LDAP server/port as a key. - */ - private static final Cache certStoreCache = Cache.newSoftMemoryCache(185); - static synchronized CertStore getInstance(LDAPCertStoreParameters params) - throws NoSuchAlgorithmException, InvalidAlgorithmParameterException { - CertStore lcs = (CertStore) certStoreCache.get(params); - if (lcs == null) { - lcs = CertStore.getInstance("LDAP", params); - certStoreCache.put(params, lcs); - } else { - if (debug != null) { - debug.println("LDAPCertStore.getInstance: cache hit"); - } - } - return lcs; - } - - /** - * Create InitialDirContext. - * - * @param server Server DNS name hosting LDAP service - * @param port Port at which server listens for requests - * @throws InvalidAlgorithmParameterException if creation fails - */ - private void createInitialDirContext(String server, int port) - throws InvalidAlgorithmParameterException { - String url = "ldap://" + server + ":" + port; - Hashtable<String,Object> env = new Hashtable<String,Object>(); - env.put(Context.INITIAL_CONTEXT_FACTORY, - "com.sun.jndi.ldap.LdapCtxFactory"); - env.put(Context.PROVIDER_URL, url); - try { - ctx = new InitialDirContext(env); - /* - * By default, follow referrals unless application has - * overridden property in an application resource file. - */ - Hashtable<?,?> currentEnv = ctx.getEnvironment(); - if (currentEnv.get(Context.REFERRAL) == null) { - ctx.addToEnvironment(Context.REFERRAL, "follow"); - } - } catch (NamingException e) { - if (debug != null) { - debug.println("LDAPCertStore.engineInit about to throw " - + "InvalidAlgorithmParameterException"); - e.printStackTrace(); - } - Exception ee = new InvalidAlgorithmParameterException - ("unable to create InitialDirContext using supplied parameters"); - ee.initCause(e); - throw (InvalidAlgorithmParameterException)ee; - } - } - - /** - * Private class encapsulating the actual LDAP operations and cache - * handling. Use: - * - * LDAPRequest request = new LDAPRequest(dn); - * request.addRequestedAttribute(CROSS_CERT); - * request.addRequestedAttribute(CA_CERT); - * byte[][] crossValues = request.getValues(CROSS_CERT); - * byte[][] caValues = request.getValues(CA_CERT); - * - * At most one LDAP request is sent for each instance created. If all - * getValues() calls can be satisfied from the cache, no request - * is sent at all. If a request is sent, all requested attributes - * are always added to the cache irrespective of whether the getValues() - * method is called. - */ - private class LDAPRequest { - - private final String name; - private Map<String, byte[][]> valueMap; - private final List<String> requestedAttributes; - - LDAPRequest(String name) { - this.name = name; - requestedAttributes = new ArrayList<String>(5); - } - - String getName() { - return name; - } - - void addRequestedAttribute(String attrId) { - if (valueMap != null) { - throw new IllegalStateException("Request already sent"); - } - requestedAttributes.add(attrId); - } - - /** - * Gets one or more binary values from an attribute. - * - * @param name the location holding the attribute - * @param attrId the attribute identifier - * @return an array of binary values (byte arrays) - * @throws NamingException if a naming exception occurs - */ - byte[][] getValues(String attrId) throws NamingException { - if (DEBUG && ((cacheHits + cacheMisses) % 50 == 0)) { - System.out.println("Cache hits: " + cacheHits + "; misses: " - + cacheMisses); - } - String cacheKey = name + "|" + attrId; - byte[][] values = (byte[][])valueCache.get(cacheKey); - if (values != null) { - cacheHits++; - return values; - } - cacheMisses++; - Map<String, byte[][]> attrs = getValueMap(); - values = attrs.get(attrId); - return values; - } - - /** - * Get a map containing the values for this request. The first time - * this method is called on an object, the LDAP request is sent, - * the results parsed and added to a private map and also to the - * cache of this LDAPCertStore. Subsequent calls return the private - * map immediately. - * - * The map contains an entry for each requested attribute. The - * attribute name is the key, values are byte[][]. If there are no - * values for that attribute, values are byte[0][]. - * - * @return the value Map - * @throws NamingException if a naming exception occurs - */ - private Map<String, byte[][]> getValueMap() throws NamingException { - if (valueMap != null) { - return valueMap; - } - if (DEBUG) { - System.out.println("Request: " + name + ":" + requestedAttributes); - requests++; - if (requests % 5 == 0) { - System.out.println("LDAP requests: " + requests); - } - } - valueMap = new HashMap<String, byte[][]>(8); - String[] attrIds = requestedAttributes.toArray(STRING0); - Attributes attrs; - try { - attrs = ctx.getAttributes(name, attrIds); - } catch (NameNotFoundException e) { - // name does not exist on this LDAP server - // treat same as not attributes found - attrs = EMPTY_ATTRIBUTES; - } - for (String attrId : requestedAttributes) { - Attribute attr = attrs.get(attrId); - byte[][] values = getAttributeValues(attr); - cacheAttribute(attrId, values); - valueMap.put(attrId, values); - } - return valueMap; - } - - /** - * Add the values to the cache. - */ - private void cacheAttribute(String attrId, byte[][] values) { - String cacheKey = name + "|" + attrId; - valueCache.put(cacheKey, values); - } - - /** - * Get the values for the given attribute. If the attribute is null - * or does not contain any values, a zero length byte array is - * returned. NOTE that it is assumed that all values are byte arrays. - */ - private byte[][] getAttributeValues(Attribute attr) - throws NamingException { - byte[][] values; - if (attr == null) { - values = BB0; - } else { - values = new byte[attr.size()][]; - int i = 0; - NamingEnumeration<?> enum_ = attr.getAll(); - while (enum_.hasMore()) { - Object obj = enum_.next(); - if (debug != null) { - if (obj instanceof String) { - debug.println("LDAPCertStore.getAttrValues() " - + "enum.next is a string!: " + obj); - } - } - byte[] value = (byte[])obj; - values[i++] = value; - } - } - return values; - } - - } - - /* - * Gets certificates from an attribute id and location in the LDAP - * directory. Returns a Collection containing only the Certificates that - * match the specified CertSelector. - * - * @param name the location holding the attribute - * @param id the attribute identifier - * @param sel a CertSelector that the Certificates must match - * @return a Collection of Certificates found - * @throws CertStoreException if an exception occurs - */ - private Collection<X509Certificate> getCertificates(LDAPRequest request, - String id, X509CertSelector sel) throws CertStoreException { - - /* fetch encoded certs from storage */ - byte[][] encodedCert; - try { - encodedCert = request.getValues(id); - } catch (NamingException namingEx) { - throw new CertStoreException(namingEx); - } - - int n = encodedCert.length; - if (n == 0) { - return Collections.<X509Certificate>emptySet(); - } - - List<X509Certificate> certs = new ArrayList<X509Certificate>(n); - /* decode certs and check if they satisfy selector */ - for (int i = 0; i < n; i++) { - ByteArrayInputStream bais = new ByteArrayInputStream(encodedCert[i]); - try { - Certificate cert = cf.generateCertificate(bais); - if (sel.match(cert)) { - certs.add((X509Certificate)cert); - } - } catch (CertificateException e) { - if (debug != null) { - debug.println("LDAPCertStore.getCertificates() encountered " - + "exception while parsing cert, skipping the bad data: "); - HexDumpEncoder encoder = new HexDumpEncoder(); - debug.println( - "[ " + encoder.encodeBuffer(encodedCert[i]) + " ]"); - } - } - } - - return certs; - } - - /* - * Gets certificate pairs from an attribute id and location in the LDAP - * directory. - * - * @param name the location holding the attribute - * @param id the attribute identifier - * @return a Collection of X509CertificatePairs found - * @throws CertStoreException if an exception occurs - */ - private Collection<X509CertificatePair> getCertPairs( - LDAPRequest request, String id) throws CertStoreException { - - /* fetch the encoded cert pairs from storage */ - byte[][] encodedCertPair; - try { - encodedCertPair = request.getValues(id); - } catch (NamingException namingEx) { - throw new CertStoreException(namingEx); - } - - int n = encodedCertPair.length; - if (n == 0) { - return Collections.<X509CertificatePair>emptySet(); - } - - List<X509CertificatePair> certPairs = - new ArrayList<X509CertificatePair>(n); - /* decode each cert pair and add it to the Collection */ - for (int i = 0; i < n; i++) { - try { - X509CertificatePair certPair = - X509CertificatePair.generateCertificatePair(encodedCertPair[i]); - certPairs.add(certPair); - } catch (CertificateException e) { - if (debug != null) { - debug.println( - "LDAPCertStore.getCertPairs() encountered exception " - + "while parsing cert, skipping the bad data: "); - HexDumpEncoder encoder = new HexDumpEncoder(); - debug.println( - "[ " + encoder.encodeBuffer(encodedCertPair[i]) + " ]"); - } - } - } - - return certPairs; - } - - /* - * Looks at certificate pairs stored in the crossCertificatePair attribute - * at the specified location in the LDAP directory. Returns a Collection - * containing all Certificates stored in the forward component that match - * the forward CertSelector and all Certificates stored in the reverse - * component that match the reverse CertSelector. - * <p> - * If either forward or reverse is null, all certificates from the - * corresponding component will be rejected. - * - * @param name the location to look in - * @param forward the forward CertSelector (or null) - * @param reverse the reverse CertSelector (or null) - * @return a Collection of Certificates found - * @throws CertStoreException if an exception occurs - */ - private Collection<X509Certificate> getMatchingCrossCerts( - LDAPRequest request, X509CertSelector forward, - X509CertSelector reverse) - throws CertStoreException { - // Get the cert pairs - Collection<X509CertificatePair> certPairs = - getCertPairs(request, CROSS_CERT); - - // Find Certificates that match and put them in a list - ArrayList<X509Certificate> matchingCerts = - new ArrayList<X509Certificate>(); - for (X509CertificatePair certPair : certPairs) { - X509Certificate cert; - if (forward != null) { - cert = certPair.getForward(); - if ((cert != null) && forward.match(cert)) { - matchingCerts.add(cert); - } - } - if (reverse != null) { - cert = certPair.getReverse(); - if ((cert != null) && reverse.match(cert)) { - matchingCerts.add(cert); - } - } - } - return matchingCerts; - } - - /** - * Returns a <code>Collection</code> of <code>Certificate</code>s that - * match the specified selector. If no <code>Certificate</code>s - * match the selector, an empty <code>Collection</code> will be returned. - * <p> - * It is not practical to search every entry in the LDAP database for - * matching <code>Certificate</code>s. Instead, the <code>CertSelector</code> - * is examined in order to determine where matching <code>Certificate</code>s - * are likely to be found (according to the PKIX LDAPv2 schema, RFC 2587). - * If the subject is specified, its directory entry is searched. If the - * issuer is specified, its directory entry is searched. If neither the - * subject nor the issuer are specified (or the selector is not an - * <code>X509CertSelector</code>), a <code>CertStoreException</code> is - * thrown. - * - * @param selector a <code>CertSelector</code> used to select which - * <code>Certificate</code>s should be returned. - * @return a <code>Collection</code> of <code>Certificate</code>s that - * match the specified selector - * @throws CertStoreException if an exception occurs - */ - public synchronized Collection<X509Certificate> engineGetCertificates - (CertSelector selector) throws CertStoreException { - if (debug != null) { - debug.println("LDAPCertStore.engineGetCertificates() selector: " - + String.valueOf(selector)); - } - - if (selector == null) { - selector = new X509CertSelector(); - } - if (!(selector instanceof X509CertSelector)) { - throw new CertStoreException("LDAPCertStore needs an X509CertSelector " + - "to find certs"); - } - X509CertSelector xsel = (X509CertSelector) selector; - int basicConstraints = xsel.getBasicConstraints(); - String subject = xsel.getSubjectAsString(); - String issuer = xsel.getIssuerAsString(); - HashSet<X509Certificate> certs = new HashSet<X509Certificate>(); - if (debug != null) { - debug.println("LDAPCertStore.engineGetCertificates() basicConstraints: " - + basicConstraints); - } - - // basicConstraints: - // -2: only EE certs accepted - // -1: no check is done - // 0: any CA certificate accepted - // >1: certificate's basicConstraints extension pathlen must match - if (subject != null) { - if (debug != null) { - debug.println("LDAPCertStore.engineGetCertificates() " - + "subject is not null"); - } - LDAPRequest request = new LDAPRequest(subject); - if (basicConstraints > -2) { - request.addRequestedAttribute(CROSS_CERT); - request.addRequestedAttribute(CA_CERT); - request.addRequestedAttribute(ARL); - if (prefetchCRLs) { - request.addRequestedAttribute(CRL); - } - } - if (basicConstraints < 0) { - request.addRequestedAttribute(USER_CERT); - } - - if (basicConstraints > -2) { - certs.addAll(getMatchingCrossCerts(request, xsel, null)); - if (debug != null) { - debug.println("LDAPCertStore.engineGetCertificates() after " - + "getMatchingCrossCerts(subject,xsel,null),certs.size(): " - + certs.size()); - } - certs.addAll(getCertificates(request, CA_CERT, xsel)); - if (debug != null) { - debug.println("LDAPCertStore.engineGetCertificates() after " - + "getCertificates(subject,CA_CERT,xsel),certs.size(): " - + certs.size()); - } - } - if (basicConstraints < 0) { - certs.addAll(getCertificates(request, USER_CERT, xsel)); - if (debug != null) { - debug.println("LDAPCertStore.engineGetCertificates() after " - + "getCertificates(subject,USER_CERT, xsel),certs.size(): " - + certs.size()); - } - } - } else { - if (debug != null) { - debug.println - ("LDAPCertStore.engineGetCertificates() subject is null"); - } - if (basicConstraints == -2) { - throw new CertStoreException("need subject to find EE certs"); - } - if (issuer == null) { - throw new CertStoreException("need subject or issuer to find certs"); - } - } - if (debug != null) { - debug.println("LDAPCertStore.engineGetCertificates() about to " - + "getMatchingCrossCerts..."); - } - if ((issuer != null) && (basicConstraints > -2)) { - LDAPRequest request = new LDAPRequest(issuer); - request.addRequestedAttribute(CROSS_CERT); - request.addRequestedAttribute(CA_CERT); - request.addRequestedAttribute(ARL); - if (prefetchCRLs) { - request.addRequestedAttribute(CRL); - } - - certs.addAll(getMatchingCrossCerts(request, null, xsel)); - if (debug != null) { - debug.println("LDAPCertStore.engineGetCertificates() after " - + "getMatchingCrossCerts(issuer,null,xsel),certs.size(): " - + certs.size()); - } - certs.addAll(getCertificates(request, CA_CERT, xsel)); - if (debug != null) { - debug.println("LDAPCertStore.engineGetCertificates() after " - + "getCertificates(issuer,CA_CERT,xsel),certs.size(): " - + certs.size()); - } - } - if (debug != null) { - debug.println("LDAPCertStore.engineGetCertificates() returning certs"); - } - return certs; - } - - /* - * Gets CRLs from an attribute id and location in the LDAP directory. - * Returns a Collection containing only the CRLs that match the - * specified CRLSelector. - * - * @param name the location holding the attribute - * @param id the attribute identifier - * @param sel a CRLSelector that the CRLs must match - * @return a Collection of CRLs found - * @throws CertStoreException if an exception occurs - */ - private Collection<X509CRL> getCRLs(LDAPRequest request, String id, - X509CRLSelector sel) throws CertStoreException { - - /* fetch the encoded crls from storage */ - byte[][] encodedCRL; - try { - encodedCRL = request.getValues(id); - } catch (NamingException namingEx) { - throw new CertStoreException(namingEx); - } - - int n = encodedCRL.length; - if (n == 0) { - return Collections.<X509CRL>emptySet(); - } - - List<X509CRL> crls = new ArrayList<X509CRL>(n); - /* decode each crl and check if it matches selector */ - for (int i = 0; i < n; i++) { - try { - CRL crl = cf.generateCRL(new ByteArrayInputStream(encodedCRL[i])); - if (sel.match(crl)) { - crls.add((X509CRL)crl); - } - } catch (CRLException e) { - if (debug != null) { - debug.println("LDAPCertStore.getCRLs() encountered exception" - + " while parsing CRL, skipping the bad data: "); - HexDumpEncoder encoder = new HexDumpEncoder(); - debug.println("[ " + encoder.encodeBuffer(encodedCRL[i]) + " ]"); - } - } - } - - return crls; - } - - /** - * Returns a <code>Collection</code> of <code>CRL</code>s that - * match the specified selector. If no <code>CRL</code>s - * match the selector, an empty <code>Collection</code> will be returned. - * <p> - * It is not practical to search every entry in the LDAP database for - * matching <code>CRL</code>s. Instead, the <code>CRLSelector</code> - * is examined in order to determine where matching <code>CRL</code>s - * are likely to be found (according to the PKIX LDAPv2 schema, RFC 2587). - * If issuerNames or certChecking are specified, the issuer's directory - * entry is searched. If neither issuerNames or certChecking are specified - * (or the selector is not an <code>X509CRLSelector</code>), a - * <code>CertStoreException</code> is thrown. - * - * @param selector A <code>CRLSelector</code> used to select which - * <code>CRL</code>s should be returned. Specify <code>null</code> - * to return all <code>CRL</code>s. - * @return A <code>Collection</code> of <code>CRL</code>s that - * match the specified selector - * @throws CertStoreException if an exception occurs - */ - public synchronized Collection<X509CRL> engineGetCRLs(CRLSelector selector) - throws CertStoreException { - if (debug != null) { - debug.println("LDAPCertStore.engineGetCRLs() selector: " - + selector); - } - // Set up selector and collection to hold CRLs - if (selector == null) { - selector = new X509CRLSelector(); - } - if (!(selector instanceof X509CRLSelector)) { - throw new CertStoreException("need X509CRLSelector to find CRLs"); - } - X509CRLSelector xsel = (X509CRLSelector) selector; - HashSet<X509CRL> crls = new HashSet<X509CRL>(); - - // Look in directory entry for issuer of cert we're checking. - Collection<Object> issuerNames; - X509Certificate certChecking = xsel.getCertificateChecking(); - if (certChecking != null) { - issuerNames = new HashSet<Object>(); - X500Principal issuer = certChecking.getIssuerX500Principal(); - issuerNames.add(issuer.getName(X500Principal.RFC2253)); - } else { - // But if we don't know which cert we're checking, try the directory - // entries of all acceptable CRL issuers - issuerNames = xsel.getIssuerNames(); - if (issuerNames == null) { - throw new CertStoreException("need issuerNames or certChecking to " - + "find CRLs"); - } - } - for (Object nameObject : issuerNames) { - String issuerName; - if (nameObject instanceof byte[]) { - try { - X500Principal issuer = new X500Principal((byte[])nameObject); - issuerName = issuer.getName(X500Principal.RFC2253); - } catch (IllegalArgumentException e) { - continue; - } - } else { - issuerName = (String)nameObject; - } - // If all we want is CA certs, try to get the (probably shorter) ARL - Collection<X509CRL> entryCRLs = Collections.<X509CRL>emptySet(); - if (certChecking == null || certChecking.getBasicConstraints() != -1) { - LDAPRequest request = new LDAPRequest(issuerName); - request.addRequestedAttribute(CROSS_CERT); - request.addRequestedAttribute(CA_CERT); - request.addRequestedAttribute(ARL); - if (prefetchCRLs) { - request.addRequestedAttribute(CRL); - } - try { - entryCRLs = getCRLs(request, ARL, xsel); - if (entryCRLs.isEmpty()) { - // no ARLs found. We assume that means that there are - // no ARLs on this server at all and prefetch the CRLs. - prefetchCRLs = true; - } else { - crls.addAll(entryCRLs); - } - } catch (CertStoreException e) { - if (debug != null) { - debug.println("LDAPCertStore.engineGetCRLs non-fatal error " - + "retrieving ARLs:" + e); - e.printStackTrace(); - } - } - } - // Otherwise, get the CRL - // if certChecking is null, we don't know if we should look in ARL or CRL - // attribute, so check both for matching CRLs. - if (entryCRLs.isEmpty() || certChecking == null) { - LDAPRequest request = new LDAPRequest(issuerName); - request.addRequestedAttribute(CRL); - entryCRLs = getCRLs(request, CRL, xsel); - crls.addAll(entryCRLs); - } - } - return crls; - } - - // converts an LDAP URI into LDAPCertStoreParameters - static LDAPCertStoreParameters getParameters(URI uri) { - String host = uri.getHost(); - if (host == null) { - return new SunLDAPCertStoreParameters(); - } else { - int port = uri.getPort(); - return (port == -1 - ? new SunLDAPCertStoreParameters(host) - : new SunLDAPCertStoreParameters(host, port)); - } - } - - /* - * Subclass of LDAPCertStoreParameters with overridden equals/hashCode - * methods. This is necessary because the parameters are used as - * keys in the LDAPCertStore cache. - */ - private static class SunLDAPCertStoreParameters - extends LDAPCertStoreParameters { - - private volatile int hashCode = 0; - - SunLDAPCertStoreParameters(String serverName, int port) { - super(serverName, port); - } - SunLDAPCertStoreParameters(String serverName) { - super(serverName); - } - SunLDAPCertStoreParameters() { - super(); - } - public boolean equals(Object obj) { - if (!(obj instanceof LDAPCertStoreParameters)) { - return false; - } - LDAPCertStoreParameters params = (LDAPCertStoreParameters) obj; - return (getPort() == params.getPort() && - getServerName().equalsIgnoreCase(params.getServerName())); - } - public int hashCode() { - if (hashCode == 0) { - int result = 17; - result = 37*result + getPort(); - result = 37*result + getServerName().toLowerCase().hashCode(); - hashCode = result; - } - return hashCode; - } - } - - /* - * This inner class wraps an existing X509CertSelector and adds - * additional criteria to match on when the certificate's subject is - * different than the LDAP Distinguished Name entry. The LDAPCertStore - * implementation uses the subject DN as the directory entry for - * looking up certificates. This can be problematic if the certificates - * that you want to fetch have a different subject DN than the entry - * where they are stored. You could set the selector's subject to the - * LDAP DN entry, but then the resulting match would fail to find the - * desired certificates because the subject DNs would not match. This - * class avoids that problem by introducing a certSubject which should - * be set to the certificate's subject DN when it is different than - * the LDAP DN. - */ - static class LDAPCertSelector extends X509CertSelector { - - private X500Principal certSubject; - private X509CertSelector selector; - private X500Principal subject; - - /** - * Creates an LDAPCertSelector. - * - * @param selector the X509CertSelector to wrap - * @param certSubject the subject DN of the certificate that you want - * to retrieve via LDAP - * @param ldapDN the LDAP DN where the certificate is stored - */ - LDAPCertSelector(X509CertSelector selector, X500Principal certSubject, - String ldapDN) throws IOException { - this.selector = selector == null ? new X509CertSelector() : selector; - this.certSubject = certSubject; - this.subject = new X500Name(ldapDN).asX500Principal(); - } - - // we only override the get (accessor methods) since the set methods - // will not be invoked by the code that uses this LDAPCertSelector. - public X509Certificate getCertificate() { - return selector.getCertificate(); - } - public BigInteger getSerialNumber() { - return selector.getSerialNumber(); - } - public X500Principal getIssuer() { - return selector.getIssuer(); - } - public String getIssuerAsString() { - return selector.getIssuerAsString(); - } - public byte[] getIssuerAsBytes() throws IOException { - return selector.getIssuerAsBytes(); - } - public X500Principal getSubject() { - // return the ldap DN - return subject; - } - public String getSubjectAsString() { - // return the ldap DN - return subject.getName(); - } - public byte[] getSubjectAsBytes() throws IOException { - // return the encoded ldap DN - return subject.getEncoded(); - } - public byte[] getSubjectKeyIdentifier() { - return selector.getSubjectKeyIdentifier(); - } - public byte[] getAuthorityKeyIdentifier() { - return selector.getAuthorityKeyIdentifier(); - } - public Date getCertificateValid() { - return selector.getCertificateValid(); - } - public Date getPrivateKeyValid() { - return selector.getPrivateKeyValid(); - } - public String getSubjectPublicKeyAlgID() { - return selector.getSubjectPublicKeyAlgID(); - } - public PublicKey getSubjectPublicKey() { - return selector.getSubjectPublicKey(); - } - public boolean[] getKeyUsage() { - return selector.getKeyUsage(); - } - public Set<String> getExtendedKeyUsage() { - return selector.getExtendedKeyUsage(); - } - public boolean getMatchAllSubjectAltNames() { - return selector.getMatchAllSubjectAltNames(); - } - public Collection<List<?>> getSubjectAlternativeNames() { - return selector.getSubjectAlternativeNames(); - } - public byte[] getNameConstraints() { - return selector.getNameConstraints(); - } - public int getBasicConstraints() { - return selector.getBasicConstraints(); - } - public Set<String> getPolicy() { - return selector.getPolicy(); - } - public Collection<List<?>> getPathToNames() { - return selector.getPathToNames(); - } - - public boolean match(Certificate cert) { - // temporarily set the subject criterion to the certSubject - // so that match will not reject the desired certificates - selector.setSubject(certSubject); - boolean match = selector.match(cert); - selector.setSubject(subject); - return match; - } - } - - /** - * This class has the same purpose as LDAPCertSelector except it is for - * X.509 CRLs. - */ - static class LDAPCRLSelector extends X509CRLSelector { - - private X509CRLSelector selector; - private Collection<X500Principal> certIssuers; - private Collection<X500Principal> issuers; - private HashSet<Object> issuerNames; - - /** - * Creates an LDAPCRLSelector. - * - * @param selector the X509CRLSelector to wrap - * @param certIssuers the issuer DNs of the CRLs that you want - * to retrieve via LDAP - * @param ldapDN the LDAP DN where the CRL is stored - */ - LDAPCRLSelector(X509CRLSelector selector, - Collection<X500Principal> certIssuers, String ldapDN) - throws IOException { - this.selector = selector == null ? new X509CRLSelector() : selector; - this.certIssuers = certIssuers; - issuerNames = new HashSet<Object>(); - issuerNames.add(ldapDN); - issuers = new HashSet<X500Principal>(); - issuers.add(new X500Name(ldapDN).asX500Principal()); - } - // we only override the get (accessor methods) since the set methods - // will not be invoked by the code that uses this LDAPCRLSelector. - public Collection<X500Principal> getIssuers() { - // return the ldap DN - return Collections.unmodifiableCollection(issuers); - } - public Collection<Object> getIssuerNames() { - // return the ldap DN - return Collections.unmodifiableCollection(issuerNames); - } - public BigInteger getMinCRL() { - return selector.getMinCRL(); - } - public BigInteger getMaxCRL() { - return selector.getMaxCRL(); - } - public Date getDateAndTime() { - return selector.getDateAndTime(); - } - public X509Certificate getCertificateChecking() { - return selector.getCertificateChecking(); - } - public boolean match(CRL crl) { - // temporarily set the issuer criterion to the certIssuers - // so that match will not reject the desired CRL - selector.setIssuers(certIssuers); - boolean match = selector.match(crl); - selector.setIssuers(issuers); - return match; - } - } -}
--- a/jdk/src/share/classes/sun/security/provider/certpath/OCSP.java Fri Oct 23 18:44:33 2009 -0700 +++ b/jdk/src/share/classes/sun/security/provider/certpath/OCSP.java Wed Oct 28 10:37:17 2009 -0700 @@ -64,6 +64,8 @@ private static final Debug debug = Debug.getInstance("certpath"); + private static final int CONNECT_TIMEOUT = 15000; // 15 seconds + private OCSP() {} /** @@ -176,6 +178,8 @@ debug.println("connecting to OCSP service at: " + url); } HttpURLConnection con = (HttpURLConnection)url.openConnection(); + con.setConnectTimeout(CONNECT_TIMEOUT); + con.setReadTimeout(CONNECT_TIMEOUT); con.setDoOutput(true); con.setDoInput(true); con.setRequestMethod("POST");
--- a/jdk/src/share/classes/sun/security/provider/certpath/OCSPChecker.java Fri Oct 23 18:44:33 2009 -0700 +++ b/jdk/src/share/classes/sun/security/provider/certpath/OCSPChecker.java Wed Oct 28 10:37:17 2009 -0700 @@ -25,7 +25,6 @@ package sun.security.provider.certpath; -import java.io.IOException; import java.math.BigInteger; import java.util.*; import java.security.AccessController; @@ -335,10 +334,11 @@ (issuerCert, currCertImpl.getSerialNumberObject()); response = OCSP.check(Collections.singletonList(certId), uri, responderCert, pkixParams.getDate()); - } catch (IOException ioe) { - // should allow this to pass if network failures are acceptable + } catch (Exception e) { + // Wrap all exceptions in CertPathValidatorException so that + // we can fallback to CRLs, if enabled. throw new CertPathValidatorException - ("Unable to send OCSP request", ioe); + ("Unable to send OCSP request", e); } RevocationStatus rs = (RevocationStatus) response.getSingleResponse(certId);
--- a/jdk/src/share/classes/sun/security/provider/certpath/URICertStore.java Fri Oct 23 18:44:33 2009 -0700 +++ b/jdk/src/share/classes/sun/security/provider/certpath/URICertStore.java Wed Oct 28 10:37:17 2009 -0700 @@ -30,6 +30,8 @@ import java.net.HttpURLConnection; import java.net.URI; import java.net.URLConnection; +import java.security.AccessController; +import java.security.PrivilegedAction; import java.security.InvalidAlgorithmParameterException; import java.security.NoSuchAlgorithmException; import java.security.Provider; @@ -121,6 +123,32 @@ private String ldapPath; /** + * Holder class to lazily load LDAPCertStoreHelper if present. + */ + private static class LDAP { + private static final String CERT_STORE_HELPER = + "sun.security.provider.certpath.ldap.LDAPCertStoreHelper"; + private static final CertStoreHelper helper = + AccessController.doPrivileged( + new PrivilegedAction<CertStoreHelper>() { + public CertStoreHelper run() { + try { + Class<?> c = Class.forName(CERT_STORE_HELPER, true, null); + return (CertStoreHelper)c.newInstance(); + } catch (ClassNotFoundException cnf) { + return null; + } catch (InstantiationException e) { + throw new AssertionError(e); + } catch (IllegalAccessException e) { + throw new AssertionError(e); + } + }}); + static CertStoreHelper helper() { + return helper; + } + } + + /** * Creates a URICertStore. * * @param parameters specifying the URI @@ -135,9 +163,10 @@ this.uri = ((URICertStoreParameters) params).uri; // if ldap URI, use an LDAPCertStore to fetch certs and CRLs if (uri.getScheme().toLowerCase().equals("ldap")) { + if (LDAP.helper() == null) + throw new NoSuchAlgorithmException("LDAP not present"); ldap = true; - ldapCertStore = - LDAPCertStore.getInstance(LDAPCertStore.getParameters(uri)); + ldapCertStore = LDAP.helper().getCertStore(uri); ldapPath = uri.getPath(); // strip off leading '/' if (ldapPath.charAt(0) == '/') { @@ -219,8 +248,7 @@ if (ldap) { X509CertSelector xsel = (X509CertSelector) selector; try { - xsel = new LDAPCertStore.LDAPCertSelector - (xsel, xsel.getSubject(), ldapPath); + xsel = LDAP.helper().wrap(xsel, xsel.getSubject(), ldapPath); } catch (IOException ioe) { throw new CertStoreException(ioe); } @@ -340,7 +368,7 @@ if (ldap) { X509CRLSelector xsel = (X509CRLSelector) selector; try { - xsel = new LDAPCertStore.LDAPCRLSelector(xsel, null, ldapPath); + xsel = LDAP.helper().wrap(xsel, null, ldapPath); } catch (IOException ioe) { throw new CertStoreException(ioe); }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/share/classes/sun/security/provider/certpath/ldap/LDAPCertStore.java Wed Oct 28 10:37:17 2009 -0700 @@ -0,0 +1,1066 @@ +/* + * Copyright 2000-2006 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 sun.security.provider.certpath.ldap; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.math.BigInteger; +import java.net.URI; +import java.util.*; +import javax.naming.Context; +import javax.naming.NamingEnumeration; +import javax.naming.NamingException; +import javax.naming.NameNotFoundException; +import javax.naming.directory.Attribute; +import javax.naming.directory.Attributes; +import javax.naming.directory.BasicAttributes; +import javax.naming.directory.DirContext; +import javax.naming.directory.InitialDirContext; + +import java.security.*; +import java.security.cert.Certificate; +import java.security.cert.*; +import javax.security.auth.x500.X500Principal; + +import sun.misc.HexDumpEncoder; +import sun.security.provider.certpath.X509CertificatePair; +import sun.security.util.Cache; +import sun.security.util.Debug; +import sun.security.x509.X500Name; +import sun.security.action.GetPropertyAction; + +/** + * A <code>CertStore</code> that retrieves <code>Certificates</code> and + * <code>CRL</code>s from an LDAP directory, using the PKIX LDAP V2 Schema + * (RFC 2587): + * <a href="http://www.ietf.org/rfc/rfc2587.txt"> + * http://www.ietf.org/rfc/rfc2587.txt</a>. + * <p> + * Before calling the {@link #engineGetCertificates engineGetCertificates} or + * {@link #engineGetCRLs engineGetCRLs} methods, the + * {@link #LDAPCertStore(CertStoreParameters) + * LDAPCertStore(CertStoreParameters)} constructor is called to create the + * <code>CertStore</code> and establish the DNS name and port of the LDAP + * server from which <code>Certificate</code>s and <code>CRL</code>s will be + * retrieved. + * <p> + * <b>Concurrent Access</b> + * <p> + * As described in the javadoc for <code>CertStoreSpi</code>, the + * <code>engineGetCertificates</code> and <code>engineGetCRLs</code> methods + * must be thread-safe. That is, multiple threads may concurrently + * invoke these methods on a single <code>LDAPCertStore</code> object + * (or more than one) with no ill effects. This allows a + * <code>CertPathBuilder</code> to search for a CRL while simultaneously + * searching for further certificates, for instance. + * <p> + * This is achieved by adding the <code>synchronized</code> keyword to the + * <code>engineGetCertificates</code> and <code>engineGetCRLs</code> methods. + * <p> + * This classes uses caching and requests multiple attributes at once to + * minimize LDAP round trips. The cache is associated with the CertStore + * instance. It uses soft references to hold the values to minimize impact + * on footprint and currently has a maximum size of 750 attributes and a + * 30 second default lifetime. + * <p> + * We always request CA certificates, cross certificate pairs, and ARLs in + * a single LDAP request when any one of them is needed. The reason is that + * we typically need all of them anyway and requesting them in one go can + * reduce the number of requests to a third. Even if we don't need them, + * these attributes are typically small enough not to cause a noticeable + * overhead. In addition, when the prefetchCRLs flag is true, we also request + * the full CRLs. It is currently false initially but set to true once any + * request for an ARL to the server returns an null value. The reason is + * that CRLs could be rather large but are rarely used. This implementation + * should improve performance in most cases. + * + * @see java.security.cert.CertStore + * + * @since 1.4 + * @author Steve Hanna + * @author Andreas Sterbenz + */ +public class LDAPCertStore extends CertStoreSpi { + + private static final Debug debug = Debug.getInstance("certpath"); + + private final static boolean DEBUG = false; + + /** + * LDAP attribute identifiers. + */ + private static final String USER_CERT = "userCertificate;binary"; + private static final String CA_CERT = "cACertificate;binary"; + private static final String CROSS_CERT = "crossCertificatePair;binary"; + private static final String CRL = "certificateRevocationList;binary"; + private static final String ARL = "authorityRevocationList;binary"; + private static final String DELTA_CRL = "deltaRevocationList;binary"; + + // Constants for various empty values + private final static String[] STRING0 = new String[0]; + + private final static byte[][] BB0 = new byte[0][]; + + private final static Attributes EMPTY_ATTRIBUTES = new BasicAttributes(); + + // cache related constants + private final static int DEFAULT_CACHE_SIZE = 750; + private final static int DEFAULT_CACHE_LIFETIME = 30; + + private final static int LIFETIME; + + private final static String PROP_LIFETIME = + "sun.security.certpath.ldap.cache.lifetime"; + + static { + String s = AccessController.doPrivileged( + new GetPropertyAction(PROP_LIFETIME)); + if (s != null) { + LIFETIME = Integer.parseInt(s); // throws NumberFormatException + } else { + LIFETIME = DEFAULT_CACHE_LIFETIME; + } + } + + /** + * The CertificateFactory used to decode certificates from + * their binary stored form. + */ + private CertificateFactory cf; + /** + * The JNDI directory context. + */ + private DirContext ctx; + + /** + * Flag indicating whether we should prefetch CRLs. + */ + private boolean prefetchCRLs = false; + + private final Cache valueCache; + + private int cacheHits = 0; + private int cacheMisses = 0; + private int requests = 0; + + /** + * Creates a <code>CertStore</code> with the specified parameters. + * For this class, the parameters object must be an instance of + * <code>LDAPCertStoreParameters</code>. + * + * @param params the algorithm parameters + * @exception InvalidAlgorithmParameterException if params is not an + * instance of <code>LDAPCertStoreParameters</code> + */ + public LDAPCertStore(CertStoreParameters params) + throws InvalidAlgorithmParameterException { + super(params); + if (!(params instanceof LDAPCertStoreParameters)) + throw new InvalidAlgorithmParameterException( + "parameters must be LDAPCertStoreParameters"); + + LDAPCertStoreParameters lparams = (LDAPCertStoreParameters) params; + + // Create InitialDirContext needed to communicate with the server + createInitialDirContext(lparams.getServerName(), lparams.getPort()); + + // Create CertificateFactory for use later on + try { + cf = CertificateFactory.getInstance("X.509"); + } catch (CertificateException e) { + throw new InvalidAlgorithmParameterException( + "unable to create CertificateFactory for X.509"); + } + if (LIFETIME == 0) { + valueCache = Cache.newNullCache(); + } else if (LIFETIME < 0) { + valueCache = Cache.newSoftMemoryCache(DEFAULT_CACHE_SIZE); + } else { + valueCache = Cache.newSoftMemoryCache(DEFAULT_CACHE_SIZE, LIFETIME); + } + } + + /** + * Returns an LDAP CertStore. This method consults a cache of + * CertStores (shared per JVM) using the LDAP server/port as a key. + */ + private static final Cache certStoreCache = Cache.newSoftMemoryCache(185); + static synchronized CertStore getInstance(LDAPCertStoreParameters params) + throws NoSuchAlgorithmException, InvalidAlgorithmParameterException { + CertStore lcs = (CertStore) certStoreCache.get(params); + if (lcs == null) { + lcs = CertStore.getInstance("LDAP", params); + certStoreCache.put(params, lcs); + } else { + if (debug != null) { + debug.println("LDAPCertStore.getInstance: cache hit"); + } + } + return lcs; + } + + /** + * Create InitialDirContext. + * + * @param server Server DNS name hosting LDAP service + * @param port Port at which server listens for requests + * @throws InvalidAlgorithmParameterException if creation fails + */ + private void createInitialDirContext(String server, int port) + throws InvalidAlgorithmParameterException { + String url = "ldap://" + server + ":" + port; + Hashtable<String,Object> env = new Hashtable<String,Object>(); + env.put(Context.INITIAL_CONTEXT_FACTORY, + "com.sun.jndi.ldap.LdapCtxFactory"); + env.put(Context.PROVIDER_URL, url); + try { + ctx = new InitialDirContext(env); + /* + * By default, follow referrals unless application has + * overridden property in an application resource file. + */ + Hashtable<?,?> currentEnv = ctx.getEnvironment(); + if (currentEnv.get(Context.REFERRAL) == null) { + ctx.addToEnvironment(Context.REFERRAL, "follow"); + } + } catch (NamingException e) { + if (debug != null) { + debug.println("LDAPCertStore.engineInit about to throw " + + "InvalidAlgorithmParameterException"); + e.printStackTrace(); + } + Exception ee = new InvalidAlgorithmParameterException + ("unable to create InitialDirContext using supplied parameters"); + ee.initCause(e); + throw (InvalidAlgorithmParameterException)ee; + } + } + + /** + * Private class encapsulating the actual LDAP operations and cache + * handling. Use: + * + * LDAPRequest request = new LDAPRequest(dn); + * request.addRequestedAttribute(CROSS_CERT); + * request.addRequestedAttribute(CA_CERT); + * byte[][] crossValues = request.getValues(CROSS_CERT); + * byte[][] caValues = request.getValues(CA_CERT); + * + * At most one LDAP request is sent for each instance created. If all + * getValues() calls can be satisfied from the cache, no request + * is sent at all. If a request is sent, all requested attributes + * are always added to the cache irrespective of whether the getValues() + * method is called. + */ + private class LDAPRequest { + + private final String name; + private Map<String, byte[][]> valueMap; + private final List<String> requestedAttributes; + + LDAPRequest(String name) { + this.name = name; + requestedAttributes = new ArrayList<String>(5); + } + + String getName() { + return name; + } + + void addRequestedAttribute(String attrId) { + if (valueMap != null) { + throw new IllegalStateException("Request already sent"); + } + requestedAttributes.add(attrId); + } + + /** + * Gets one or more binary values from an attribute. + * + * @param name the location holding the attribute + * @param attrId the attribute identifier + * @return an array of binary values (byte arrays) + * @throws NamingException if a naming exception occurs + */ + byte[][] getValues(String attrId) throws NamingException { + if (DEBUG && ((cacheHits + cacheMisses) % 50 == 0)) { + System.out.println("Cache hits: " + cacheHits + "; misses: " + + cacheMisses); + } + String cacheKey = name + "|" + attrId; + byte[][] values = (byte[][])valueCache.get(cacheKey); + if (values != null) { + cacheHits++; + return values; + } + cacheMisses++; + Map<String, byte[][]> attrs = getValueMap(); + values = attrs.get(attrId); + return values; + } + + /** + * Get a map containing the values for this request. The first time + * this method is called on an object, the LDAP request is sent, + * the results parsed and added to a private map and also to the + * cache of this LDAPCertStore. Subsequent calls return the private + * map immediately. + * + * The map contains an entry for each requested attribute. The + * attribute name is the key, values are byte[][]. If there are no + * values for that attribute, values are byte[0][]. + * + * @return the value Map + * @throws NamingException if a naming exception occurs + */ + private Map<String, byte[][]> getValueMap() throws NamingException { + if (valueMap != null) { + return valueMap; + } + if (DEBUG) { + System.out.println("Request: " + name + ":" + requestedAttributes); + requests++; + if (requests % 5 == 0) { + System.out.println("LDAP requests: " + requests); + } + } + valueMap = new HashMap<String, byte[][]>(8); + String[] attrIds = requestedAttributes.toArray(STRING0); + Attributes attrs; + try { + attrs = ctx.getAttributes(name, attrIds); + } catch (NameNotFoundException e) { + // name does not exist on this LDAP server + // treat same as not attributes found + attrs = EMPTY_ATTRIBUTES; + } + for (String attrId : requestedAttributes) { + Attribute attr = attrs.get(attrId); + byte[][] values = getAttributeValues(attr); + cacheAttribute(attrId, values); + valueMap.put(attrId, values); + } + return valueMap; + } + + /** + * Add the values to the cache. + */ + private void cacheAttribute(String attrId, byte[][] values) { + String cacheKey = name + "|" + attrId; + valueCache.put(cacheKey, values); + } + + /** + * Get the values for the given attribute. If the attribute is null + * or does not contain any values, a zero length byte array is + * returned. NOTE that it is assumed that all values are byte arrays. + */ + private byte[][] getAttributeValues(Attribute attr) + throws NamingException { + byte[][] values; + if (attr == null) { + values = BB0; + } else { + values = new byte[attr.size()][]; + int i = 0; + NamingEnumeration<?> enum_ = attr.getAll(); + while (enum_.hasMore()) { + Object obj = enum_.next(); + if (debug != null) { + if (obj instanceof String) { + debug.println("LDAPCertStore.getAttrValues() " + + "enum.next is a string!: " + obj); + } + } + byte[] value = (byte[])obj; + values[i++] = value; + } + } + return values; + } + + } + + /* + * Gets certificates from an attribute id and location in the LDAP + * directory. Returns a Collection containing only the Certificates that + * match the specified CertSelector. + * + * @param name the location holding the attribute + * @param id the attribute identifier + * @param sel a CertSelector that the Certificates must match + * @return a Collection of Certificates found + * @throws CertStoreException if an exception occurs + */ + private Collection<X509Certificate> getCertificates(LDAPRequest request, + String id, X509CertSelector sel) throws CertStoreException { + + /* fetch encoded certs from storage */ + byte[][] encodedCert; + try { + encodedCert = request.getValues(id); + } catch (NamingException namingEx) { + throw new CertStoreException(namingEx); + } + + int n = encodedCert.length; + if (n == 0) { + return Collections.<X509Certificate>emptySet(); + } + + List<X509Certificate> certs = new ArrayList<X509Certificate>(n); + /* decode certs and check if they satisfy selector */ + for (int i = 0; i < n; i++) { + ByteArrayInputStream bais = new ByteArrayInputStream(encodedCert[i]); + try { + Certificate cert = cf.generateCertificate(bais); + if (sel.match(cert)) { + certs.add((X509Certificate)cert); + } + } catch (CertificateException e) { + if (debug != null) { + debug.println("LDAPCertStore.getCertificates() encountered " + + "exception while parsing cert, skipping the bad data: "); + HexDumpEncoder encoder = new HexDumpEncoder(); + debug.println( + "[ " + encoder.encodeBuffer(encodedCert[i]) + " ]"); + } + } + } + + return certs; + } + + /* + * Gets certificate pairs from an attribute id and location in the LDAP + * directory. + * + * @param name the location holding the attribute + * @param id the attribute identifier + * @return a Collection of X509CertificatePairs found + * @throws CertStoreException if an exception occurs + */ + private Collection<X509CertificatePair> getCertPairs( + LDAPRequest request, String id) throws CertStoreException { + + /* fetch the encoded cert pairs from storage */ + byte[][] encodedCertPair; + try { + encodedCertPair = request.getValues(id); + } catch (NamingException namingEx) { + throw new CertStoreException(namingEx); + } + + int n = encodedCertPair.length; + if (n == 0) { + return Collections.<X509CertificatePair>emptySet(); + } + + List<X509CertificatePair> certPairs = + new ArrayList<X509CertificatePair>(n); + /* decode each cert pair and add it to the Collection */ + for (int i = 0; i < n; i++) { + try { + X509CertificatePair certPair = + X509CertificatePair.generateCertificatePair(encodedCertPair[i]); + certPairs.add(certPair); + } catch (CertificateException e) { + if (debug != null) { + debug.println( + "LDAPCertStore.getCertPairs() encountered exception " + + "while parsing cert, skipping the bad data: "); + HexDumpEncoder encoder = new HexDumpEncoder(); + debug.println( + "[ " + encoder.encodeBuffer(encodedCertPair[i]) + " ]"); + } + } + } + + return certPairs; + } + + /* + * Looks at certificate pairs stored in the crossCertificatePair attribute + * at the specified location in the LDAP directory. Returns a Collection + * containing all Certificates stored in the forward component that match + * the forward CertSelector and all Certificates stored in the reverse + * component that match the reverse CertSelector. + * <p> + * If either forward or reverse is null, all certificates from the + * corresponding component will be rejected. + * + * @param name the location to look in + * @param forward the forward CertSelector (or null) + * @param reverse the reverse CertSelector (or null) + * @return a Collection of Certificates found + * @throws CertStoreException if an exception occurs + */ + private Collection<X509Certificate> getMatchingCrossCerts( + LDAPRequest request, X509CertSelector forward, + X509CertSelector reverse) + throws CertStoreException { + // Get the cert pairs + Collection<X509CertificatePair> certPairs = + getCertPairs(request, CROSS_CERT); + + // Find Certificates that match and put them in a list + ArrayList<X509Certificate> matchingCerts = + new ArrayList<X509Certificate>(); + for (X509CertificatePair certPair : certPairs) { + X509Certificate cert; + if (forward != null) { + cert = certPair.getForward(); + if ((cert != null) && forward.match(cert)) { + matchingCerts.add(cert); + } + } + if (reverse != null) { + cert = certPair.getReverse(); + if ((cert != null) && reverse.match(cert)) { + matchingCerts.add(cert); + } + } + } + return matchingCerts; + } + + /** + * Returns a <code>Collection</code> of <code>Certificate</code>s that + * match the specified selector. If no <code>Certificate</code>s + * match the selector, an empty <code>Collection</code> will be returned. + * <p> + * It is not practical to search every entry in the LDAP database for + * matching <code>Certificate</code>s. Instead, the <code>CertSelector</code> + * is examined in order to determine where matching <code>Certificate</code>s + * are likely to be found (according to the PKIX LDAPv2 schema, RFC 2587). + * If the subject is specified, its directory entry is searched. If the + * issuer is specified, its directory entry is searched. If neither the + * subject nor the issuer are specified (or the selector is not an + * <code>X509CertSelector</code>), a <code>CertStoreException</code> is + * thrown. + * + * @param selector a <code>CertSelector</code> used to select which + * <code>Certificate</code>s should be returned. + * @return a <code>Collection</code> of <code>Certificate</code>s that + * match the specified selector + * @throws CertStoreException if an exception occurs + */ + public synchronized Collection<X509Certificate> engineGetCertificates + (CertSelector selector) throws CertStoreException { + if (debug != null) { + debug.println("LDAPCertStore.engineGetCertificates() selector: " + + String.valueOf(selector)); + } + + if (selector == null) { + selector = new X509CertSelector(); + } + if (!(selector instanceof X509CertSelector)) { + throw new CertStoreException("LDAPCertStore needs an X509CertSelector " + + "to find certs"); + } + X509CertSelector xsel = (X509CertSelector) selector; + int basicConstraints = xsel.getBasicConstraints(); + String subject = xsel.getSubjectAsString(); + String issuer = xsel.getIssuerAsString(); + HashSet<X509Certificate> certs = new HashSet<X509Certificate>(); + if (debug != null) { + debug.println("LDAPCertStore.engineGetCertificates() basicConstraints: " + + basicConstraints); + } + + // basicConstraints: + // -2: only EE certs accepted + // -1: no check is done + // 0: any CA certificate accepted + // >1: certificate's basicConstraints extension pathlen must match + if (subject != null) { + if (debug != null) { + debug.println("LDAPCertStore.engineGetCertificates() " + + "subject is not null"); + } + LDAPRequest request = new LDAPRequest(subject); + if (basicConstraints > -2) { + request.addRequestedAttribute(CROSS_CERT); + request.addRequestedAttribute(CA_CERT); + request.addRequestedAttribute(ARL); + if (prefetchCRLs) { + request.addRequestedAttribute(CRL); + } + } + if (basicConstraints < 0) { + request.addRequestedAttribute(USER_CERT); + } + + if (basicConstraints > -2) { + certs.addAll(getMatchingCrossCerts(request, xsel, null)); + if (debug != null) { + debug.println("LDAPCertStore.engineGetCertificates() after " + + "getMatchingCrossCerts(subject,xsel,null),certs.size(): " + + certs.size()); + } + certs.addAll(getCertificates(request, CA_CERT, xsel)); + if (debug != null) { + debug.println("LDAPCertStore.engineGetCertificates() after " + + "getCertificates(subject,CA_CERT,xsel),certs.size(): " + + certs.size()); + } + } + if (basicConstraints < 0) { + certs.addAll(getCertificates(request, USER_CERT, xsel)); + if (debug != null) { + debug.println("LDAPCertStore.engineGetCertificates() after " + + "getCertificates(subject,USER_CERT, xsel),certs.size(): " + + certs.size()); + } + } + } else { + if (debug != null) { + debug.println + ("LDAPCertStore.engineGetCertificates() subject is null"); + } + if (basicConstraints == -2) { + throw new CertStoreException("need subject to find EE certs"); + } + if (issuer == null) { + throw new CertStoreException("need subject or issuer to find certs"); + } + } + if (debug != null) { + debug.println("LDAPCertStore.engineGetCertificates() about to " + + "getMatchingCrossCerts..."); + } + if ((issuer != null) && (basicConstraints > -2)) { + LDAPRequest request = new LDAPRequest(issuer); + request.addRequestedAttribute(CROSS_CERT); + request.addRequestedAttribute(CA_CERT); + request.addRequestedAttribute(ARL); + if (prefetchCRLs) { + request.addRequestedAttribute(CRL); + } + + certs.addAll(getMatchingCrossCerts(request, null, xsel)); + if (debug != null) { + debug.println("LDAPCertStore.engineGetCertificates() after " + + "getMatchingCrossCerts(issuer,null,xsel),certs.size(): " + + certs.size()); + } + certs.addAll(getCertificates(request, CA_CERT, xsel)); + if (debug != null) { + debug.println("LDAPCertStore.engineGetCertificates() after " + + "getCertificates(issuer,CA_CERT,xsel),certs.size(): " + + certs.size()); + } + } + if (debug != null) { + debug.println("LDAPCertStore.engineGetCertificates() returning certs"); + } + return certs; + } + + /* + * Gets CRLs from an attribute id and location in the LDAP directory. + * Returns a Collection containing only the CRLs that match the + * specified CRLSelector. + * + * @param name the location holding the attribute + * @param id the attribute identifier + * @param sel a CRLSelector that the CRLs must match + * @return a Collection of CRLs found + * @throws CertStoreException if an exception occurs + */ + private Collection<X509CRL> getCRLs(LDAPRequest request, String id, + X509CRLSelector sel) throws CertStoreException { + + /* fetch the encoded crls from storage */ + byte[][] encodedCRL; + try { + encodedCRL = request.getValues(id); + } catch (NamingException namingEx) { + throw new CertStoreException(namingEx); + } + + int n = encodedCRL.length; + if (n == 0) { + return Collections.<X509CRL>emptySet(); + } + + List<X509CRL> crls = new ArrayList<X509CRL>(n); + /* decode each crl and check if it matches selector */ + for (int i = 0; i < n; i++) { + try { + CRL crl = cf.generateCRL(new ByteArrayInputStream(encodedCRL[i])); + if (sel.match(crl)) { + crls.add((X509CRL)crl); + } + } catch (CRLException e) { + if (debug != null) { + debug.println("LDAPCertStore.getCRLs() encountered exception" + + " while parsing CRL, skipping the bad data: "); + HexDumpEncoder encoder = new HexDumpEncoder(); + debug.println("[ " + encoder.encodeBuffer(encodedCRL[i]) + " ]"); + } + } + } + + return crls; + } + + /** + * Returns a <code>Collection</code> of <code>CRL</code>s that + * match the specified selector. If no <code>CRL</code>s + * match the selector, an empty <code>Collection</code> will be returned. + * <p> + * It is not practical to search every entry in the LDAP database for + * matching <code>CRL</code>s. Instead, the <code>CRLSelector</code> + * is examined in order to determine where matching <code>CRL</code>s + * are likely to be found (according to the PKIX LDAPv2 schema, RFC 2587). + * If issuerNames or certChecking are specified, the issuer's directory + * entry is searched. If neither issuerNames or certChecking are specified + * (or the selector is not an <code>X509CRLSelector</code>), a + * <code>CertStoreException</code> is thrown. + * + * @param selector A <code>CRLSelector</code> used to select which + * <code>CRL</code>s should be returned. Specify <code>null</code> + * to return all <code>CRL</code>s. + * @return A <code>Collection</code> of <code>CRL</code>s that + * match the specified selector + * @throws CertStoreException if an exception occurs + */ + public synchronized Collection<X509CRL> engineGetCRLs(CRLSelector selector) + throws CertStoreException { + if (debug != null) { + debug.println("LDAPCertStore.engineGetCRLs() selector: " + + selector); + } + // Set up selector and collection to hold CRLs + if (selector == null) { + selector = new X509CRLSelector(); + } + if (!(selector instanceof X509CRLSelector)) { + throw new CertStoreException("need X509CRLSelector to find CRLs"); + } + X509CRLSelector xsel = (X509CRLSelector) selector; + HashSet<X509CRL> crls = new HashSet<X509CRL>(); + + // Look in directory entry for issuer of cert we're checking. + Collection<Object> issuerNames; + X509Certificate certChecking = xsel.getCertificateChecking(); + if (certChecking != null) { + issuerNames = new HashSet<Object>(); + X500Principal issuer = certChecking.getIssuerX500Principal(); + issuerNames.add(issuer.getName(X500Principal.RFC2253)); + } else { + // But if we don't know which cert we're checking, try the directory + // entries of all acceptable CRL issuers + issuerNames = xsel.getIssuerNames(); + if (issuerNames == null) { + throw new CertStoreException("need issuerNames or certChecking to " + + "find CRLs"); + } + } + for (Object nameObject : issuerNames) { + String issuerName; + if (nameObject instanceof byte[]) { + try { + X500Principal issuer = new X500Principal((byte[])nameObject); + issuerName = issuer.getName(X500Principal.RFC2253); + } catch (IllegalArgumentException e) { + continue; + } + } else { + issuerName = (String)nameObject; + } + // If all we want is CA certs, try to get the (probably shorter) ARL + Collection<X509CRL> entryCRLs = Collections.<X509CRL>emptySet(); + if (certChecking == null || certChecking.getBasicConstraints() != -1) { + LDAPRequest request = new LDAPRequest(issuerName); + request.addRequestedAttribute(CROSS_CERT); + request.addRequestedAttribute(CA_CERT); + request.addRequestedAttribute(ARL); + if (prefetchCRLs) { + request.addRequestedAttribute(CRL); + } + try { + entryCRLs = getCRLs(request, ARL, xsel); + if (entryCRLs.isEmpty()) { + // no ARLs found. We assume that means that there are + // no ARLs on this server at all and prefetch the CRLs. + prefetchCRLs = true; + } else { + crls.addAll(entryCRLs); + } + } catch (CertStoreException e) { + if (debug != null) { + debug.println("LDAPCertStore.engineGetCRLs non-fatal error " + + "retrieving ARLs:" + e); + e.printStackTrace(); + } + } + } + // Otherwise, get the CRL + // if certChecking is null, we don't know if we should look in ARL or CRL + // attribute, so check both for matching CRLs. + if (entryCRLs.isEmpty() || certChecking == null) { + LDAPRequest request = new LDAPRequest(issuerName); + request.addRequestedAttribute(CRL); + entryCRLs = getCRLs(request, CRL, xsel); + crls.addAll(entryCRLs); + } + } + return crls; + } + + // converts an LDAP URI into LDAPCertStoreParameters + static LDAPCertStoreParameters getParameters(URI uri) { + String host = uri.getHost(); + if (host == null) { + return new SunLDAPCertStoreParameters(); + } else { + int port = uri.getPort(); + return (port == -1 + ? new SunLDAPCertStoreParameters(host) + : new SunLDAPCertStoreParameters(host, port)); + } + } + + /* + * Subclass of LDAPCertStoreParameters with overridden equals/hashCode + * methods. This is necessary because the parameters are used as + * keys in the LDAPCertStore cache. + */ + private static class SunLDAPCertStoreParameters + extends LDAPCertStoreParameters { + + private volatile int hashCode = 0; + + SunLDAPCertStoreParameters(String serverName, int port) { + super(serverName, port); + } + SunLDAPCertStoreParameters(String serverName) { + super(serverName); + } + SunLDAPCertStoreParameters() { + super(); + } + public boolean equals(Object obj) { + if (!(obj instanceof LDAPCertStoreParameters)) { + return false; + } + LDAPCertStoreParameters params = (LDAPCertStoreParameters) obj; + return (getPort() == params.getPort() && + getServerName().equalsIgnoreCase(params.getServerName())); + } + public int hashCode() { + if (hashCode == 0) { + int result = 17; + result = 37*result + getPort(); + result = 37*result + getServerName().toLowerCase().hashCode(); + hashCode = result; + } + return hashCode; + } + } + + /* + * This inner class wraps an existing X509CertSelector and adds + * additional criteria to match on when the certificate's subject is + * different than the LDAP Distinguished Name entry. The LDAPCertStore + * implementation uses the subject DN as the directory entry for + * looking up certificates. This can be problematic if the certificates + * that you want to fetch have a different subject DN than the entry + * where they are stored. You could set the selector's subject to the + * LDAP DN entry, but then the resulting match would fail to find the + * desired certificates because the subject DNs would not match. This + * class avoids that problem by introducing a certSubject which should + * be set to the certificate's subject DN when it is different than + * the LDAP DN. + */ + static class LDAPCertSelector extends X509CertSelector { + + private X500Principal certSubject; + private X509CertSelector selector; + private X500Principal subject; + + /** + * Creates an LDAPCertSelector. + * + * @param selector the X509CertSelector to wrap + * @param certSubject the subject DN of the certificate that you want + * to retrieve via LDAP + * @param ldapDN the LDAP DN where the certificate is stored + */ + LDAPCertSelector(X509CertSelector selector, X500Principal certSubject, + String ldapDN) throws IOException { + this.selector = selector == null ? new X509CertSelector() : selector; + this.certSubject = certSubject; + this.subject = new X500Name(ldapDN).asX500Principal(); + } + + // we only override the get (accessor methods) since the set methods + // will not be invoked by the code that uses this LDAPCertSelector. + public X509Certificate getCertificate() { + return selector.getCertificate(); + } + public BigInteger getSerialNumber() { + return selector.getSerialNumber(); + } + public X500Principal getIssuer() { + return selector.getIssuer(); + } + public String getIssuerAsString() { + return selector.getIssuerAsString(); + } + public byte[] getIssuerAsBytes() throws IOException { + return selector.getIssuerAsBytes(); + } + public X500Principal getSubject() { + // return the ldap DN + return subject; + } + public String getSubjectAsString() { + // return the ldap DN + return subject.getName(); + } + public byte[] getSubjectAsBytes() throws IOException { + // return the encoded ldap DN + return subject.getEncoded(); + } + public byte[] getSubjectKeyIdentifier() { + return selector.getSubjectKeyIdentifier(); + } + public byte[] getAuthorityKeyIdentifier() { + return selector.getAuthorityKeyIdentifier(); + } + public Date getCertificateValid() { + return selector.getCertificateValid(); + } + public Date getPrivateKeyValid() { + return selector.getPrivateKeyValid(); + } + public String getSubjectPublicKeyAlgID() { + return selector.getSubjectPublicKeyAlgID(); + } + public PublicKey getSubjectPublicKey() { + return selector.getSubjectPublicKey(); + } + public boolean[] getKeyUsage() { + return selector.getKeyUsage(); + } + public Set<String> getExtendedKeyUsage() { + return selector.getExtendedKeyUsage(); + } + public boolean getMatchAllSubjectAltNames() { + return selector.getMatchAllSubjectAltNames(); + } + public Collection<List<?>> getSubjectAlternativeNames() { + return selector.getSubjectAlternativeNames(); + } + public byte[] getNameConstraints() { + return selector.getNameConstraints(); + } + public int getBasicConstraints() { + return selector.getBasicConstraints(); + } + public Set<String> getPolicy() { + return selector.getPolicy(); + } + public Collection<List<?>> getPathToNames() { + return selector.getPathToNames(); + } + + public boolean match(Certificate cert) { + // temporarily set the subject criterion to the certSubject + // so that match will not reject the desired certificates + selector.setSubject(certSubject); + boolean match = selector.match(cert); + selector.setSubject(subject); + return match; + } + } + + /** + * This class has the same purpose as LDAPCertSelector except it is for + * X.509 CRLs. + */ + static class LDAPCRLSelector extends X509CRLSelector { + + private X509CRLSelector selector; + private Collection<X500Principal> certIssuers; + private Collection<X500Principal> issuers; + private HashSet<Object> issuerNames; + + /** + * Creates an LDAPCRLSelector. + * + * @param selector the X509CRLSelector to wrap + * @param certIssuers the issuer DNs of the CRLs that you want + * to retrieve via LDAP + * @param ldapDN the LDAP DN where the CRL is stored + */ + LDAPCRLSelector(X509CRLSelector selector, + Collection<X500Principal> certIssuers, String ldapDN) + throws IOException { + this.selector = selector == null ? new X509CRLSelector() : selector; + this.certIssuers = certIssuers; + issuerNames = new HashSet<Object>(); + issuerNames.add(ldapDN); + issuers = new HashSet<X500Principal>(); + issuers.add(new X500Name(ldapDN).asX500Principal()); + } + // we only override the get (accessor methods) since the set methods + // will not be invoked by the code that uses this LDAPCRLSelector. + public Collection<X500Principal> getIssuers() { + // return the ldap DN + return Collections.unmodifiableCollection(issuers); + } + public Collection<Object> getIssuerNames() { + // return the ldap DN + return Collections.unmodifiableCollection(issuerNames); + } + public BigInteger getMinCRL() { + return selector.getMinCRL(); + } + public BigInteger getMaxCRL() { + return selector.getMaxCRL(); + } + public Date getDateAndTime() { + return selector.getDateAndTime(); + } + public X509Certificate getCertificateChecking() { + return selector.getCertificateChecking(); + } + public boolean match(CRL crl) { + // temporarily set the issuer criterion to the certIssuers + // so that match will not reject the desired CRL + selector.setIssuers(certIssuers); + boolean match = selector.match(crl); + selector.setIssuers(issuers); + return match; + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/share/classes/sun/security/provider/certpath/ldap/LDAPCertStoreHelper.java Wed Oct 28 10:37:17 2009 -0700 @@ -0,0 +1,73 @@ +/* + * 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. + */ + +package sun.security.provider.certpath.ldap; + +import java.net.URI; +import java.util.Collection; +import java.security.NoSuchAlgorithmException; +import java.security.InvalidAlgorithmParameterException; +import java.security.cert.CertStore; +import java.security.cert.X509CertSelector; +import java.security.cert.X509CRLSelector; +import javax.security.auth.x500.X500Principal; +import java.io.IOException; + +import sun.security.provider.certpath.CertStoreHelper; + +/** + * LDAP implementation of CertStoreHelper. + */ + +public class LDAPCertStoreHelper + implements CertStoreHelper +{ + public LDAPCertStoreHelper() { } + + @Override + public CertStore getCertStore(URI uri) + throws NoSuchAlgorithmException, InvalidAlgorithmParameterException + { + return LDAPCertStore.getInstance(LDAPCertStore.getParameters(uri)); + } + + @Override + public X509CertSelector wrap(X509CertSelector selector, + X500Principal certSubject, + String ldapDN) + throws IOException + { + return new LDAPCertStore.LDAPCertSelector(selector, certSubject, ldapDN); + } + + @Override + public X509CRLSelector wrap(X509CRLSelector selector, + Collection<X500Principal> certIssuers, + String ldapDN) + throws IOException + { + return new LDAPCertStore.LDAPCRLSelector(selector, certIssuers, ldapDN); + } +}
--- a/jdk/src/solaris/classes/sun/nio/fs/SolarisAclFileAttributeView.java Fri Oct 23 18:44:33 2009 -0700 +++ b/jdk/src/solaris/classes/sun/nio/fs/SolarisAclFileAttributeView.java Wed Oct 28 10:37:17 2009 -0700 @@ -104,11 +104,11 @@ int uid; if (user.isSpecial()) { uid = -1; - if (who.getName().equals(UnixUserPrincipals.SPECIAL_OWNER.getName())) + if (who == UnixUserPrincipals.SPECIAL_OWNER) flags |= ACE_OWNER; - else if (who.getName().equals(UnixUserPrincipals.SPECIAL_GROUP.getName())) - flags |= ACE_GROUP; - else if (who.getName().equals(UnixUserPrincipals.SPECIAL_EVERYONE.getName())) + else if (who == UnixUserPrincipals.SPECIAL_GROUP) + flags |= (ACE_GROUP | ACE_IDENTIFIER_GROUP); + else if (who == UnixUserPrincipals.SPECIAL_EVERYONE) flags |= ACE_EVERYONE; else throw new AssertionError("Unable to map special identifier"); @@ -281,7 +281,7 @@ aceFlags.add(AclEntryFlag.DIRECTORY_INHERIT); if ((flags & ACE_NO_PROPAGATE_INHERIT_ACE) > 0) aceFlags.add(AclEntryFlag.NO_PROPAGATE_INHERIT); - if ((flags & ACE_INHERIT_ONLY_ACE ) > 0) + if ((flags & ACE_INHERIT_ONLY_ACE) > 0) aceFlags.add(AclEntryFlag.INHERIT_ONLY); // build the ACL entry and add it to the list
--- a/jdk/src/solaris/classes/sun/nio/fs/UnixDirectoryStream.java Fri Oct 23 18:44:33 2009 -0700 +++ b/jdk/src/solaris/classes/sun/nio/fs/UnixDirectoryStream.java Wed Oct 28 10:37:17 2009 -0700 @@ -235,7 +235,8 @@ @Override public void remove() { if (isClosed) { - throw new ClosedDirectoryStreamException(); + throwAsConcurrentModificationException(new + ClosedDirectoryStreamException()); } Path entry; synchronized (this) {
--- a/jdk/src/windows/classes/sun/nio/fs/WindowsDirectoryStream.java Fri Oct 23 18:44:33 2009 -0700 +++ b/jdk/src/windows/classes/sun/nio/fs/WindowsDirectoryStream.java Wed Oct 28 10:37:17 2009 -0700 @@ -179,7 +179,7 @@ synchronized (closeLock) { if (!isOpen) throwAsConcurrentModificationException(new - IllegalStateException("Directory stream is closed")); + ClosedDirectoryStreamException()); try { name = FindNextFile(handle, findDataBuffer.address()); if (name == null) { @@ -236,7 +236,8 @@ @Override public void remove() { if (!isOpen) { - throw new IllegalStateException("Directory stream is closed"); + throwAsConcurrentModificationException(new + ClosedDirectoryStreamException()); } Path entry; synchronized (this) {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/com/sun/net/httpserver/bugs/B6886436.java Wed Oct 28 10:37:17 2009 -0700 @@ -0,0 +1,93 @@ +/* + * 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 6886436 + * @summary + */ + +import com.sun.net.httpserver.*; + +import java.util.*; +import java.util.concurrent.*; +import java.util.logging.*; +import java.io.*; +import java.net.*; + +public class B6886436 { + + public static void main (String[] args) throws Exception { + Logger logger = Logger.getLogger ("com.sun.net.httpserver"); + ConsoleHandler c = new ConsoleHandler(); + c.setLevel (Level.WARNING); + logger.addHandler (c); + logger.setLevel (Level.WARNING); + Handler handler = new Handler(); + InetSocketAddress addr = new InetSocketAddress (0); + HttpServer server = HttpServer.create (addr, 0); + HttpContext ctx = server.createContext ("/test", handler); + ExecutorService executor = Executors.newCachedThreadPool(); + server.setExecutor (executor); + server.start (); + + URL url = new URL ("http://localhost:"+server.getAddress().getPort()+"/test/foo.html"); + HttpURLConnection urlc = (HttpURLConnection)url.openConnection (); + try { + InputStream is = urlc.getInputStream(); + while (is.read()!= -1) ; + is.close (); + urlc = (HttpURLConnection)url.openConnection (); + urlc.setReadTimeout (3000); + is = urlc.getInputStream(); + while (is.read()!= -1); + is.close (); + + } catch (IOException e) { + server.stop(2); + executor.shutdown(); + throw new RuntimeException ("Test failed"); + } + server.stop(2); + executor.shutdown(); + System.out.println ("OK"); + } + + public static boolean error = false; + + static class Handler implements HttpHandler { + int invocation = 1; + public void handle (HttpExchange t) + throws IOException + { + InputStream is = t.getRequestBody(); + Headers map = t.getRequestHeaders(); + Headers rmap = t.getResponseHeaders(); + while (is.read () != -1) ; + is.close(); + // send a 204 response with an empty chunked body + t.sendResponseHeaders (204, 0); + t.close(); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/lang/Double/ToString.java Wed Oct 28 10:37:17 2009 -0700 @@ -0,0 +1,39 @@ +/* + * 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 4428022 + * @summary Tests for Double.toString + * @author Andrew Haley <aph@redhat.com> + */ + +public class ToString { + + public static void main(String args[]) { + if (!Double.toString(0.001).equals("0.001")) + throw new RuntimeException("Double.toString(0.001) is not \"0.001\""); + if (!Double.toString(0.002).equals("0.002")) + throw new RuntimeException("Double.toString(0.001) is not \"0.002\""); + } +}
--- a/jdk/test/java/lang/management/RuntimeMXBean/GetSystemProperties.java Fri Oct 23 18:44:33 2009 -0700 +++ b/jdk/test/java/lang/management/RuntimeMXBean/GetSystemProperties.java Wed Oct 28 10:37:17 2009 -0700 @@ -49,6 +49,21 @@ private static final String VALUE4 = "test.property.value4"; public static void main(String[] argv) throws Exception { + // Save a copy of the original system properties + Properties props = System.getProperties(); + + try { + // replace the system Properties object for any modification + // in case jtreg caches a copy + System.setProperties(new Properties(props)); + runTest(); + } finally { + // restore original system properties + System.setProperties(props); + } + } + + private static void runTest() throws Exception { RuntimeMXBean mbean = ManagementFactory.getRuntimeMXBean(); // Print all system properties @@ -88,7 +103,10 @@ Map<String,String> props2 = mbean.getSystemProperties(); // expect the system properties returned should be // same as the one before adding KEY3 and KEY4 - props1.equals(props2); + if (!props1.equals(props2)) { + throw new RuntimeException("Two copies of system properties " + + "are expected to be equal"); + } System.out.println("Test passed."); }
--- a/jdk/test/java/lang/management/RuntimeMXBean/PropertiesTest.java Fri Oct 23 18:44:33 2009 -0700 +++ b/jdk/test/java/lang/management/RuntimeMXBean/PropertiesTest.java Wed Oct 28 10:37:17 2009 -0700 @@ -40,8 +40,21 @@ public class PropertiesTest { private static int NUM_MYPROPS = 3; public static void main(String[] argv) throws Exception { - Properties sysProps = System.getProperties(); + // Save a copy of the original system properties + Properties props = System.getProperties(); + try { + // replace the system Properties object for any modification + // in case jtreg caches a copy + System.setProperties(new Properties(props)); + runTest(props.size()); + } finally { + // restore original system properties + System.setProperties(props); + } + } + + private static void runTest(int sysPropsCount) throws Exception { // Create a new system properties using the old one // as the defaults Properties myProps = new Properties( System.getProperties() ); @@ -65,10 +78,10 @@ System.out.println(i++ + ": " + key + " : " + value); } - if (props.size() != NUM_MYPROPS + sysProps.size()) { + if (props.size() != NUM_MYPROPS + sysPropsCount) { throw new RuntimeException("Test Failed: " + "Expected number of properties = " + - NUM_MYPROPS + sysProps.size() + + NUM_MYPROPS + sysPropsCount + " but found = " + props.size()); } }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/lang/reflect/DefaultAccessibility.java Wed Oct 28 10:37:17 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 6648344 + * @summary Test that default accessibility is false + * @author Joseph D. Darcy + */ + +import java.lang.reflect.*; + +public class DefaultAccessibility { + private DefaultAccessibility() { + super(); + } + + private static int f = 42; + + public static void main(String... args) throws Exception { + Class<?> daClass = (new DefaultAccessibility()).getClass(); + + int elementCount = 0; + for(Constructor<?> ctor : daClass.getDeclaredConstructors()) { + elementCount++; + if (ctor.isAccessible()) + throw new RuntimeException("Unexpected accessibility for constructor " + + ctor); + } + + for(Method method : daClass.getDeclaredMethods()) { + elementCount++; + if (method.isAccessible()) + throw new RuntimeException("Unexpected accessibility for method " + + method); + } + + for(Field field : daClass.getDeclaredFields()) { + elementCount++; + if (field.isAccessible()) + throw new RuntimeException("Unexpected accessibility for field " + + field); + } + + if (elementCount < 3) + throw new RuntimeException("Expected at least three members; only found " + + elementCount); + } +}
--- a/jdk/test/java/nio/file/DirectoryStream/Basic.java Fri Oct 23 18:44:33 2009 -0700 +++ b/jdk/test/java/nio/file/DirectoryStream/Basic.java Wed Oct 28 10:37:17 2009 -0700 @@ -154,8 +154,10 @@ stream.close(); // test IllegalStateException + dir.resolve(foo).createFile(); stream = dir.newDirectoryStream(); i = stream.iterator(); + i.next(); try { stream.iterator(); throw new RuntimeException("IllegalStateException not thrown as expected"); @@ -172,17 +174,26 @@ throw new RuntimeException("ConcurrentModificationException not thrown as expected"); } catch (ConcurrentModificationException x) { Throwable t = x.getCause(); - if (!(t instanceof IllegalStateException)) - throw new RuntimeException("Cause is not IllegalStateException as expected"); + if (!(t instanceof ClosedDirectoryStreamException)) + throw new RuntimeException("Cause is not ClosedDirectoryStreamException as expected"); } try { i.next(); - throw new RuntimeException("IllegalStateException not thrown as expected"); + throw new RuntimeException("ConcurrentModificationException not thrown as expected"); } catch (ConcurrentModificationException x) { Throwable t = x.getCause(); - if (!(t instanceof IllegalStateException)) - throw new RuntimeException("Cause is not IllegalStateException as expected"); + if (!(t instanceof ClosedDirectoryStreamException)) + throw new RuntimeException("Cause is not ClosedDirectoryStreamException as expected"); } + try { + i.remove(); + throw new RuntimeException("ConcurrentModificationException not thrown as expected"); + } catch (ConcurrentModificationException x) { + Throwable t = x.getCause(); + if (!(t instanceof ClosedDirectoryStreamException)) + throw new RuntimeException("Cause is not ClosedDirectoryStreamException as expected"); + } + } public static void main(String[] args) throws IOException {
--- a/jdk/test/java/nio/file/Files/ContentType.java Fri Oct 23 18:44:33 2009 -0700 +++ b/jdk/test/java/nio/file/Files/ContentType.java Wed Oct 28 10:37:17 2009 -0700 @@ -26,7 +26,7 @@ * @summary Unit test for probeContentType method * @library .. * @build ContentType SimpleFileTypeDetector - * @run main ContentType + * @run main/othervm ContentType */ import java.nio.file.*;
--- a/jdk/test/java/nio/file/Path/CheckPermissions.java Fri Oct 23 18:44:33 2009 -0700 +++ b/jdk/test/java/nio/file/Path/CheckPermissions.java Wed Oct 28 10:37:17 2009 -0700 @@ -25,6 +25,8 @@ * @bug 6866804 * @summary Unit test for java.nio.file.Path * @library .. + * @build CheckPermissions + * @run main/othervm CheckPermissions */ import java.nio.ByteBuffer;
--- a/jdk/test/java/nio/file/Path/CopyAndMove.java Fri Oct 23 18:44:33 2009 -0700 +++ b/jdk/test/java/nio/file/Path/CopyAndMove.java Wed Oct 28 10:37:17 2009 -0700 @@ -92,7 +92,6 @@ { assertTrue(attrs1.isReadOnly() == attrs2.isReadOnly()); assertTrue(attrs1.isHidden() == attrs2.isHidden()); - assertTrue(attrs1.isArchive() == attrs2.isArchive()); assertTrue(attrs1.isSystem() == attrs2.isSystem()); }
--- a/jdk/test/java/nio/file/attribute/AclFileAttributeView/Basic.java Fri Oct 23 18:44:33 2009 -0700 +++ b/jdk/test/java/nio/file/attribute/AclFileAttributeView/Basic.java Wed Oct 28 10:37:17 2009 -0700 @@ -22,7 +22,7 @@ */ /* @test - * @bug 4313887 6838333 + * @bug 4313887 6838333 6891404 * @summary Unit test for java.nio.file.attribute.AclFileAttribueView * @library ../.. */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/util/Objects/BasicObjectsTest.java Wed Oct 28 10:37:17 2009 -0700 @@ -0,0 +1,155 @@ +/* + * 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 6797535 + * @summary Basic tests for methods in java.util.Objects + * @author Joseph D. Darcy + */ + +import java.util.*; + +public class BasicObjectsTest { + public static void main(String... args) { + int errors = 0; + errors += testEquals(); + errors += testHashCode(); + errors += testToString(); + errors += testCompare(); + errors += testNonNull(); + if (errors > 0 ) + throw new RuntimeException(); + } + + private static int testEquals() { + int errors = 0; + Object[] values = {null, "42", 42}; + for(int i = 0; i < values.length; i++) + for(int j = 0; j < values.length; j++) { + boolean expected = (i == j); + Object a = values[i]; + Object b = values[j]; + boolean result = Objects.equals(a, b); + if (result != expected) { + errors++; + System.err.printf("When equating %s to %s, got %b instead of %b%n.", + a, b, result, expected); + } + } + return errors; + } + + private static int testHashCode() { + int errors = 0; + errors += (Objects.hashCode(null) == 0 ) ? 0 : 1; + String s = "42"; + errors += (Objects.hashCode(s) == s.hashCode() ) ? 0 : 1; + return errors; + } + + private static int testToString() { + int errors = 0; + errors += ("null".equals(Objects.toString(null)) ) ? 0 : 1; + String s = "Some string"; + errors += (s.equals(Objects.toString(s)) ) ? 0 : 1; + return errors; + } + + private static int testCompare() { + int errors = 0; + String[] values = {"e. e. cummings", "zzz"}; + String[] VALUES = {"E. E. Cummings", "ZZZ"}; + errors += compareTest(null, null, 0); + for(int i = 0; i < values.length; i++) { + String a = values[i]; + errors += compareTest(a, a, 0); + for(int j = 0; j < VALUES.length; j++) { + int expected = Integer.compare(i, j); + String b = VALUES[j]; + errors += compareTest(a, b, expected); + } + } + return errors; + } + + private static int compareTest(String a, String b, int expected) { + int errors = 0; + int result = Objects.compare(a, b, String.CASE_INSENSITIVE_ORDER); + if (Integer.signum(result) != Integer.signum(expected)) { + errors++; + System.err.printf("When comparing %s to %s, got %d instead of %d%n.", + a, b, result, expected); + } + return errors; + } + + private static int testNonNull() { + int errors = 0; + String s; + + // Test 1-arg variant + try { + s = Objects.nonNull("pants"); + if (s != "pants") { + System.err.printf("1-arg non-null failed to return its arg"); + errors++; + } + } catch (NullPointerException e) { + System.err.printf("1-arg nonNull threw unexpected NPE"); + errors++; + } + + try { + s = Objects.nonNull(null); + System.err.printf("1-arg nonNull failed to throw NPE"); + errors++; + } catch (NullPointerException e) { + // Expected + } + + // Test 2-arg variant + try { + s = Objects.nonNull("pants", "trousers"); + if (s != "pants") { + System.err.printf("2-arg nonNull failed to return its arg"); + errors++; + } + } catch (NullPointerException e) { + System.err.printf("2-arg nonNull threw unexpected NPE"); + errors++; + } + + try { + s = Objects.nonNull(null, "pantaloons"); + System.err.printf("2-arg nonNull failed to throw NPE"); + errors++; + } catch (NullPointerException e) { + if (e.getMessage() != "pantaloons") { + System.err.printf("2-arg nonNull threw NPE w/ bad detail msg"); + errors++; + } + } + return errors; + } +}
--- a/langtools/.hgtags Fri Oct 23 18:44:33 2009 -0700 +++ b/langtools/.hgtags Wed Oct 28 10:37:17 2009 -0700 @@ -48,3 +48,4 @@ 33c8c38e1757006c17d80499fb3347102501fae5 jdk7-b71 261c54b2312ed26d6ec45c675831375460250519 jdk7-b72 9596dff460935f09684c11d156ce591f92584f0d jdk7-b73 +1a66b08deed0459054b5b1bea3dfbead30d258fa jdk7-b74
--- a/langtools/src/share/classes/com/sun/tools/classfile/ConstantPool.java Fri Oct 23 18:44:33 2009 -0700 +++ b/langtools/src/share/classes/com/sun/tools/classfile/ConstantPool.java Wed Oct 28 10:37:17 2009 -0700 @@ -369,14 +369,33 @@ return 3; } + /** + * Get the raw value of the class referenced by this constant pool entry. + * This will either be the name of the class, in internal form, or a + * descriptor for an array class. + * @return the raw value of the class + */ public String getName() throws ConstantPoolException { return cp.getUTF8Value(name_index); } + /** + * If this constant pool entry identifies either a class or interface type, + * or a possibly multi-dimensional array of a class of interface type, + * return the name of the class or interface in internal form. Otherwise, + * (i.e. if this is a possibly multi-dimensional array of a primitive type), + * return null. + * @return the base class or interface name + */ public String getBaseName() throws ConstantPoolException { String name = getName(); - int index = name.indexOf("[L") + 1; - return name.substring(index); + if (name.startsWith("[")) { + int index = name.indexOf("[L"); + if (index == -1) + return null; + return name.substring(index + 2, name.length() - 1); + } else + return name; } public int getDimensionCount() throws ConstantPoolException {
--- a/langtools/src/share/classes/com/sun/tools/classfile/Signature.java Fri Oct 23 18:44:33 2009 -0700 +++ b/langtools/src/share/classes/com/sun/tools/classfile/Signature.java Wed Oct 28 10:37:17 2009 -0700 @@ -27,6 +27,7 @@ import java.util.ArrayList; import java.util.List; +import com.sun.tools.classfile.Type.*; /** * See JVMS3 4.4.4. @@ -50,19 +51,19 @@ @Override public int getParameterCount(ConstantPool constant_pool) throws ConstantPoolException { - Type.MethodType m = (Type.MethodType) getType(constant_pool); - return m.argTypes.size(); + MethodType m = (MethodType) getType(constant_pool); + return m.paramTypes.size(); } @Override public String getParameterTypes(ConstantPool constant_pool) throws ConstantPoolException { - Type.MethodType m = (Type.MethodType) getType(constant_pool); + MethodType m = (MethodType) getType(constant_pool); StringBuilder sb = new StringBuilder(); sb.append("("); String sep = ""; - for (Type argType: m.argTypes) { + for (Type paramType: m.paramTypes) { sb.append(sep); - sb.append(argType); + sb.append(paramType); sep = ", "; } sb.append(")"); @@ -71,7 +72,7 @@ @Override public String getReturnType(ConstantPool constant_pool) throws ConstantPoolException { - Type.MethodType m = (Type.MethodType) getType(constant_pool); + MethodType m = (MethodType) getType(constant_pool); return m.returnType.toString(); } @@ -84,12 +85,12 @@ this.sig = sig; sigp = 0; - List<Type> typeArgTypes = null; + List<TypeParamType> typeParamTypes = null; if (sig.charAt(sigp) == '<') - typeArgTypes = parseTypeArgTypes(); + typeParamTypes = parseTypeParamTypes(); if (sig.charAt(sigp) == '(') { - List<Type> argTypes = parseTypeSignatures(')'); + List<Type> paramTypes = parseTypeSignatures(')'); Type returnType = parseTypeSignature(); List<Type> throwsTypes = null; while (sigp < sig.length() && sig.charAt(sigp) == '^') { @@ -98,16 +99,19 @@ throwsTypes = new ArrayList<Type>(); throwsTypes.add(parseTypeSignature()); } - return new Type.MethodType(typeArgTypes, argTypes, returnType, throwsTypes); + return new MethodType(typeParamTypes, paramTypes, returnType, throwsTypes); } else { Type t = parseTypeSignature(); - if (typeArgTypes == null && sigp == sig.length()) + if (typeParamTypes == null && sigp == sig.length()) return t; Type superclass = t; - List<Type> superinterfaces = new ArrayList<Type>(); - while (sigp < sig.length()) + List<Type> superinterfaces = null; + while (sigp < sig.length()) { + if (superinterfaces == null) + superinterfaces = new ArrayList<Type>(); superinterfaces.add(parseTypeSignature()); - return new Type.ClassSigType(typeArgTypes, superclass, superinterfaces); + } + return new ClassSigType(typeParamTypes, superclass, superinterfaces); } } @@ -116,61 +120,61 @@ switch (sig.charAt(sigp)) { case 'B': sigp++; - return new Type.SimpleType("byte"); + return new SimpleType("byte"); case 'C': sigp++; - return new Type.SimpleType("char"); + return new SimpleType("char"); case 'D': sigp++; - return new Type.SimpleType("double"); + return new SimpleType("double"); case 'F': sigp++; - return new Type.SimpleType("float"); + return new SimpleType("float"); case 'I': sigp++; - return new Type.SimpleType("int"); + return new SimpleType("int"); case 'J': sigp++; - return new Type.SimpleType("long"); + return new SimpleType("long"); case 'L': return parseClassTypeSignature(); case 'S': sigp++; - return new Type.SimpleType("short"); + return new SimpleType("short"); case 'T': return parseTypeVariableSignature(); case 'V': sigp++; - return new Type.SimpleType("void"); + return new SimpleType("void"); case 'Z': sigp++; - return new Type.SimpleType("boolean"); + return new SimpleType("boolean"); case '[': sigp++; - return new Type.ArrayType(parseTypeSignature()); + return new ArrayType(parseTypeSignature()); case '*': sigp++; - return new Type.WildcardType(); + return new WildcardType(); case '+': sigp++; - return new Type.WildcardType("extends", parseTypeSignature()); + return new WildcardType(WildcardType.Kind.EXTENDS, parseTypeSignature()); case '-': sigp++; - return new Type.WildcardType("super", parseTypeSignature()); + return new WildcardType(WildcardType.Kind.SUPER, parseTypeSignature()); default: throw new IllegalStateException(debugInfo()); @@ -194,30 +198,22 @@ private Type parseClassTypeSignatureRest() { StringBuilder sb = new StringBuilder(); - Type t = null; - char sigch; - while (true) { + List<Type> argTypes = null; + ClassType t = null; + char sigch ; + + do { switch (sigch = sig.charAt(sigp)) { - case '/': - sigp++; - sb.append("."); + case '<': + argTypes = parseTypeSignatures('>'); break; case '.': - sigp++; - if (t == null) - t = new Type.SimpleType(sb.toString()); - return new Type.InnerClassType(t, parseClassTypeSignatureRest()); - case ';': sigp++; - if (t == null) - t = new Type.SimpleType(sb.toString()); - return t; - - case '<': - List<Type> argTypes = parseTypeSignatures('>'); - t = new Type.ClassType(sb.toString(), argTypes); + t = new ClassType(t, sb.toString(), argTypes); + sb.setLength(0); + argTypes = null; break; default: @@ -225,21 +221,22 @@ sb.append(sigch); break; } - } + } while (sigch != ';'); + + return t; } - private List<Type> parseTypeArgTypes() { + private List<TypeParamType> parseTypeParamTypes() { assert sig.charAt(sigp) == '<'; sigp++; - List<Type> types = null; - types = new ArrayList<Type>(); + List<TypeParamType> types = new ArrayList<TypeParamType>(); while (sig.charAt(sigp) != '>') - types.add(parseTypeArgType()); + types.add(parseTypeParamType()); sigp++; return types; } - private Type parseTypeArgType() { + private TypeParamType parseTypeParamType() { int sep = sig.indexOf(":", sigp); String name = sig.substring(sigp, sep); Type classBound = null; @@ -253,13 +250,13 @@ interfaceBounds = new ArrayList<Type>(); interfaceBounds.add(parseTypeSignature()); } - return new Type.TypeArgType(name, classBound, interfaceBounds); + return new TypeParamType(name, classBound, interfaceBounds); } private Type parseTypeVariableSignature() { sigp++; int sep = sig.indexOf(';', sigp); - Type t = new Type.SimpleType(sig.substring(sigp, sep)); + Type t = new SimpleType(sig.substring(sigp, sep)); sigp = sep + 1; return t; }
--- a/langtools/src/share/classes/com/sun/tools/classfile/Type.java Fri Oct 23 18:44:33 2009 -0700 +++ b/langtools/src/share/classes/com/sun/tools/classfile/Type.java Wed Oct 28 10:37:17 2009 -0700 @@ -31,6 +31,9 @@ import java.util.Set; /* + * Family of classes used to represent the parsed form of a {@link Descriptor} + * or {@link Signature}. + * * <p><b>This is NOT part of any API supported by Sun Microsystems. If * you write code that depends on this, you do so at your own risk. * This code and its internal interfaces are subject to change or @@ -62,11 +65,26 @@ R visitMethodType(MethodType type, P p); R visitClassSigType(ClassSigType type, P p); R visitClassType(ClassType type, P p); - R visitInnerClassType(InnerClassType type, P p); - R visitTypeArgType(TypeArgType type, P p); + R visitTypeParamType(TypeParamType type, P p); R visitWildcardType(WildcardType type, P p); } + /** + * Represents a type signature with a simple name. The name may be that of a + * primitive type, such "{@code int}, {@code float}, etc + * or that of a type argument, such as {@code T}, {@code K}, {@code V}, etc. + * + * See: + * JVMS 4.3.2 + * BaseType: + * {@code B}, {@code C}, {@code D}, {@code F}, {@code I}, + * {@code J}, {@code S}, {@code Z}; + * VoidDescriptor: + * {@code V}; + * JVMS 4.3.4 + * TypeVariableSignature: + * {@code T} Identifier {@code ;} + */ public static class SimpleType extends Type { public SimpleType(String name) { this.name = name; @@ -91,6 +109,14 @@ public final String name; } + /** + * Represents an array type signature. + * + * See: + * JVMS 4.3.4 + * ArrayTypeSignature: + * {@code [} TypeSignature {@code ]} + */ public static class ArrayType extends Type { public ArrayType(Type elemType) { this.elemType = elemType; @@ -108,17 +134,26 @@ public final Type elemType; } + /** + * Represents a method type signature. + * + * See; + * JVMS 4.3.4 + * MethodTypeSignature: + * FormalTypeParameters_opt {@code (} TypeSignature* {@code)} ReturnType + * ThrowsSignature* + */ public static class MethodType extends Type { - public MethodType(List<? extends Type> argTypes, Type resultType) { - this(null, argTypes, resultType, null); + public MethodType(List<? extends Type> paramTypes, Type resultType) { + this(null, paramTypes, resultType, null); } - public MethodType(List<? extends Type> typeArgTypes, - List<? extends Type> argTypes, + public MethodType(List<? extends TypeParamType> typeParamTypes, + List<? extends Type> paramTypes, Type returnType, List<? extends Type> throwsTypes) { - this.typeArgTypes = typeArgTypes; - this.argTypes = argTypes; + this.typeParamTypes = typeParamTypes; + this.paramTypes = paramTypes; this.returnType = returnType; this.throwsTypes = throwsTypes; } @@ -130,22 +165,32 @@ @Override public String toString() { StringBuilder sb = new StringBuilder(); - appendIfNotEmpty(sb, "<", typeArgTypes, "> "); + appendIfNotEmpty(sb, "<", typeParamTypes, "> "); sb.append(returnType); - append(sb, " (", argTypes, ")"); + append(sb, " (", paramTypes, ")"); appendIfNotEmpty(sb, " throws ", throwsTypes, ""); return sb.toString(); } - public final List<? extends Type> typeArgTypes; - public final List<? extends Type> argTypes; + public final List<? extends TypeParamType> typeParamTypes; + public final List<? extends Type> paramTypes; public final Type returnType; public final List<? extends Type> throwsTypes; } + /** + * Represents a class signature. These describe the signature of + * a class that has type arguments. + * + * See: + * JVMS 4.3.4 + * ClassSignature: + * FormalTypeParameters_opt SuperclassSignature SuperinterfaceSignature* + */ public static class ClassSigType extends Type { - public ClassSigType(List<Type> typeArgTypes, Type superclassType, List<Type> superinterfaceTypes) { - this.typeArgTypes = typeArgTypes; + public ClassSigType(List<TypeParamType> typeParamTypes, Type superclassType, + List<Type> superinterfaceTypes) { + this.typeParamTypes = typeParamTypes; this.superclassType = superclassType; this.superinterfaceTypes = superinterfaceTypes; } @@ -157,7 +202,7 @@ @Override public String toString() { StringBuilder sb = new StringBuilder(); - appendIfNotEmpty(sb, "<", typeArgTypes, ">"); + appendIfNotEmpty(sb, "<", typeParamTypes, ">"); if (superclassType != null) { sb.append(" extends "); sb.append(superclassType); @@ -166,13 +211,30 @@ return sb.toString(); } - public final List<Type> typeArgTypes; + public final List<TypeParamType> typeParamTypes; public final Type superclassType; public final List<Type> superinterfaceTypes; } + /** + * Represents a class type signature. This is used to represent a + * reference to a class, such as in a field, parameter, return type, etc. + * + * See: + * JVMS 4.3.4 + * ClassTypeSignature: + * {@code L} PackageSpecifier_opt SimpleClassTypeSignature + * ClassTypeSignatureSuffix* {@code ;} + * PackageSpecifier: + * Identifier {@code /} PackageSpecifier* + * SimpleClassTypeSignature: + * Identifier TypeArguments_opt } + * ClassTypeSignatureSuffix: + * {@code .} SimpleClassTypeSignature + */ public static class ClassType extends Type { - public ClassType(String name, List<Type> typeArgs) { + public ClassType(ClassType outerType, String name, List<Type> typeArgs) { + this.outerType = outerType; this.name = name; this.typeArgs = typeArgs; } @@ -181,47 +243,54 @@ return visitor.visitClassType(this, data); } + public String getBinaryName() { + if (outerType == null) + return name; + else + return (outerType.getBinaryName() + "$" + name); + } + @Override public String toString() { StringBuilder sb = new StringBuilder(); + if (outerType != null) { + sb.append(outerType); + sb.append("."); + } sb.append(name); appendIfNotEmpty(sb, "<", typeArgs, ">"); return sb.toString(); } + public final ClassType outerType; public final String name; public final List<Type> typeArgs; } - - public static class InnerClassType extends Type { - public InnerClassType(Type outerType, Type innerType) { - this.outerType = outerType; - this.innerType = innerType; - } - - public <R, D> R accept(Visitor<R, D> visitor, D data) { - return visitor.visitInnerClassType(this, data); - } - - @Override - public String toString() { - return outerType + "." + innerType; - } - - public final Type outerType; - public final Type innerType; - } - - public static class TypeArgType extends Type { - public TypeArgType(String name, Type classBound, List<Type> interfaceBounds) { + /** + * Represents a FormalTypeParameter. These are used to declare the type + * parameters for generic classes and methods. + * + * See: + * JVMS 4.3.4 + * FormalTypeParameters: + * {@code <} FormalTypeParameter+ {@code >} + * FormalTypeParameter: + * Identifier ClassBound InterfaceBound* + * ClassBound: + * {@code :} FieldTypeSignature_opt + * InterfaceBound: + * {@code :} FieldTypeSignature + */ + public static class TypeParamType extends Type { + public TypeParamType(String name, Type classBound, List<Type> interfaceBounds) { this.name = name; this.classBound = classBound; this.interfaceBounds = interfaceBounds; } public <R, D> R accept(Visitor<R, D> visitor, D data) { - return visitor.visitTypeArgType(this, data); + return visitor.visitTypeParamType(this, data); } @Override @@ -249,12 +318,25 @@ public final List<Type> interfaceBounds; } + /** + * Represents a wildcard type argument. A type argument that is not a + * wildcard type argument will be represented by a ClassType, ArrayType, etc. + * + * See: + * JVMS 4.3.4 + * TypeArgument: + * WildcardIndicator_opt FieldTypeSignature + * {@code *} + * WildcardIndicator: + * {@code +} + * {@code -} + */ public static class WildcardType extends Type { + public enum Kind { UNBOUNDED, EXTENDS, SUPER }; public WildcardType() { - this(null, null); + this(Kind.UNBOUNDED, null); } - - public WildcardType(String kind, Type boundType) { + public WildcardType(Kind kind, Type boundType) { this.kind = kind; this.boundType = boundType; } @@ -265,13 +347,19 @@ @Override public String toString() { - if (kind == null) - return "?"; - else - return "? " + kind + " " + boundType; + switch (kind) { + case UNBOUNDED: + return "?"; + case EXTENDS: + return "? extends " + boundType; + case SUPER: + return "? super " + boundType; + default: + throw new AssertionError(); + } } - public final String kind; + public final Kind kind; public final Type boundType; } }
--- a/langtools/src/share/classes/com/sun/tools/javac/code/Symbol.java Fri Oct 23 18:44:33 2009 -0700 +++ b/langtools/src/share/classes/com/sun/tools/javac/code/Symbol.java Wed Oct 28 10:37:17 2009 -0700 @@ -1212,25 +1212,58 @@ public List<VarSymbol> params() { owner.complete(); if (params == null) { - List<Name> names = savedParameterNames; + // If ClassReader.saveParameterNames has been set true, then + // savedParameterNames will be set to a list of names that + // matches the types in type.getParameterTypes(). If any names + // were not found in the class file, those names in the list will + // be set to the empty name. + // If ClassReader.saveParameterNames has been set false, then + // savedParameterNames will be null. + List<Name> paramNames = savedParameterNames; savedParameterNames = null; - if (names == null) { - names = List.nil(); - int i = 0; - for (Type t : type.getParameterTypes()) - names = names.prepend(name.table.fromString("arg" + i++)); - names = names.reverse(); - } + // discard the provided names if the list of names is the wrong size. + if (paramNames == null || paramNames.size() != type.getParameterTypes().size()) + paramNames = List.nil(); ListBuffer<VarSymbol> buf = new ListBuffer<VarSymbol>(); + List<Name> remaining = paramNames; + // assert: remaining and paramNames are both empty or both + // have same cardinality as type.getParameterTypes() + int i = 0; for (Type t : type.getParameterTypes()) { - buf.append(new VarSymbol(PARAMETER, names.head, t, this)); - names = names.tail; + Name paramName; + if (remaining.isEmpty()) { + // no names for any parameters available + paramName = createArgName(i, paramNames); + } else { + paramName = remaining.head; + remaining = remaining.tail; + if (paramName.isEmpty()) { + // no name for this specific parameter + paramName = createArgName(i, paramNames); + } + } + buf.append(new VarSymbol(PARAMETER, paramName, t, this)); + i++; } params = buf.toList(); } return params; } + // Create a name for the argument at position 'index' that is not in + // the exclude list. In normal use, either no names will have been + // provided, in which case the exclude list is empty, or all the names + // will have been provided, in which case this method will not be called. + private Name createArgName(int index, List<Name> exclude) { + String prefix = "arg"; + while (true) { + Name argName = name.table.fromString(prefix + index); + if (!exclude.contains(argName)) + return argName; + prefix += "$"; + } + } + public Symbol asMemberOf(Type site, Types types) { return new MethodSymbol(flags_field, name, types.memberType(site, this), owner); }
--- a/langtools/src/share/classes/com/sun/tools/javac/file/BaseFileObject.java Fri Oct 23 18:44:33 2009 -0700 +++ b/langtools/src/share/classes/com/sun/tools/javac/file/BaseFileObject.java Wed Oct 28 10:37:17 2009 -0700 @@ -120,6 +120,14 @@ } + // force subtypes to define equals + @Override + public abstract boolean equals(Object other); + + // force subtypes to define hashCode + @Override + public abstract int hashCode(); + /** The file manager that created this JavaFileObject. */ protected final JavacFileManager fileManager; }
--- a/langtools/src/share/classes/com/sun/tools/javac/file/JavacFileManager.java Fri Oct 23 18:44:33 2009 -0700 +++ b/langtools/src/share/classes/com/sun/tools/javac/file/JavacFileManager.java Wed Oct 28 10:37:17 2009 -0700 @@ -968,7 +968,7 @@ } else { File siblingDir = null; if (sibling != null && sibling instanceof RegularFileObject) { - siblingDir = ((RegularFileObject)sibling).f.getParentFile(); + siblingDir = ((RegularFileObject)sibling).file.getParentFile(); } return new RegularFileObject(this, new File(siblingDir, fileName.basename())); }
--- a/langtools/src/share/classes/com/sun/tools/javac/file/RegularFileObject.java Fri Oct 23 18:44:33 2009 -0700 +++ b/langtools/src/share/classes/com/sun/tools/javac/file/RegularFileObject.java Wed Oct 28 10:37:17 2009 -0700 @@ -33,6 +33,8 @@ import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.Writer; +import java.lang.ref.Reference; +import java.lang.ref.SoftReference; import java.net.URI; import java.nio.ByteBuffer; import java.nio.CharBuffer; @@ -53,7 +55,8 @@ */ private boolean hasParents = false; private String name; - final File f; + final File file; + private Reference<File> absFileRef; public RegularFileObject(JavacFileManager fileManager, File f) { this(fileManager, f.getName(), f); @@ -65,17 +68,17 @@ throw new IllegalArgumentException("directories not supported"); } this.name = name; - this.f = f; + this.file = f; } @Override public URI toUri() { - return f.toURI().normalize(); + return file.toURI().normalize(); } @Override public String getName() { - return f.getPath(); + return file.getPath(); } @Override @@ -90,20 +93,20 @@ @Override public InputStream openInputStream() throws IOException { - return new FileInputStream(f); + return new FileInputStream(file); } @Override public OutputStream openOutputStream() throws IOException { ensureParentDirectoriesExist(); - return new FileOutputStream(f); + return new FileOutputStream(file); } @Override public CharBuffer getCharContent(boolean ignoreEncodingErrors) throws IOException { CharBuffer cb = fileManager.getCachedContent(this); if (cb == null) { - InputStream in = new FileInputStream(f); + InputStream in = new FileInputStream(file); try { ByteBuffer bb = fileManager.makeByteBuffer(in); JavaFileObject prev = fileManager.log.useSource(this); @@ -126,17 +129,17 @@ @Override public Writer openWriter() throws IOException { ensureParentDirectoriesExist(); - return new OutputStreamWriter(new FileOutputStream(f), fileManager.getEncodingName()); + return new OutputStreamWriter(new FileOutputStream(file), fileManager.getEncodingName()); } @Override public long getLastModified() { - return f.lastModified(); + return file.lastModified(); } @Override public boolean delete() { - return f.delete(); + return file.delete(); } @Override @@ -146,7 +149,7 @@ @Override protected String inferBinaryName(Iterable<? extends File> path) { - String fPath = f.getPath(); + String fPath = file.getPath(); //System.err.println("RegularFileObject " + file + " " +r.getPath()); for (File dir: path) { //System.err.println("dir: " + dir); @@ -178,7 +181,7 @@ if (name.equalsIgnoreCase(n)) { try { // allow for Windows - return f.getCanonicalFile().getName().equals(n); + return file.getCanonicalFile().getName().equals(n); } catch (IOException e) { } } @@ -187,7 +190,7 @@ private void ensureParentDirectoriesExist() throws IOException { if (!hasParents) { - File parent = f.getParentFile(); + File parent = file.getParentFile(); if (parent != null && !parent.exists()) { if (!parent.mkdirs()) { if (!parent.exists() || !parent.isDirectory()) { @@ -199,21 +202,34 @@ } } + /** + * Check if two file objects are equal. + * Two RegularFileObjects are equal if the absolute paths of the underlying + * files are equal. + */ @Override public boolean equals(Object other) { - if (!(other instanceof RegularFileObject)) { + if (this == other) + return true; + + if (!(other instanceof RegularFileObject)) return false; - } + RegularFileObject o = (RegularFileObject) other; - try { - return f.equals(o.f) || f.getCanonicalFile().equals(o.f.getCanonicalFile()); - } catch (IOException e) { - return false; - } + return getAbsoluteFile().equals(o.getAbsoluteFile()); } @Override public int hashCode() { - return f.hashCode(); + return getAbsoluteFile().hashCode(); + } + + private File getAbsoluteFile() { + File absFile = (absFileRef == null ? null : absFileRef.get()); + if (absFile == null) { + absFile = file.getAbsoluteFile(); + absFileRef = new SoftReference<File>(absFile); + } + return absFile; } }
--- a/langtools/src/share/classes/com/sun/tools/javac/file/SymbolArchive.java Fri Oct 23 18:44:33 2009 -0700 +++ b/langtools/src/share/classes/com/sun/tools/javac/file/SymbolArchive.java Wed Oct 28 10:37:17 2009 -0700 @@ -76,13 +76,13 @@ @Override public JavaFileObject getFileObject(RelativeDirectory subdirectory, String file) { RelativeDirectory prefix_subdir = new RelativeDirectory(prefix, subdirectory.path); - ZipEntry ze = new RelativeFile(prefix_subdir, file).getZipEntry(zdir); + ZipEntry ze = new RelativeFile(prefix_subdir, file).getZipEntry(zfile); return new SymbolFileObject(this, file, ze); } @Override public String toString() { - return "SymbolArchive[" + zdir.getName() + "]"; + return "SymbolArchive[" + zfile.getName() + "]"; } /**
--- a/langtools/src/share/classes/com/sun/tools/javac/file/ZipArchive.java Fri Oct 23 18:44:33 2009 -0700 +++ b/langtools/src/share/classes/com/sun/tools/javac/file/ZipArchive.java Wed Oct 28 10:37:17 2009 -0700 @@ -47,6 +47,8 @@ import com.sun.tools.javac.file.RelativePath.RelativeDirectory; import com.sun.tools.javac.file.RelativePath.RelativeFile; import com.sun.tools.javac.util.List; +import java.lang.ref.Reference; +import java.lang.ref.SoftReference; /** * <p><b>This is NOT part of any API supported by Sun Microsystems. @@ -56,20 +58,20 @@ */ public class ZipArchive implements Archive { - public ZipArchive(JavacFileManager fm, ZipFile zdir) throws IOException { - this(fm, zdir, true); + public ZipArchive(JavacFileManager fm, ZipFile zfile) throws IOException { + this(fm, zfile, true); } - protected ZipArchive(JavacFileManager fm, ZipFile zdir, boolean initMap) throws IOException { + protected ZipArchive(JavacFileManager fm, ZipFile zfile, boolean initMap) throws IOException { this.fileManager = fm; - this.zdir = zdir; + this.zfile = zfile; this.map = new HashMap<RelativeDirectory,List<String>>(); if (initMap) initMap(); } protected void initMap() throws IOException { - for (Enumeration<? extends ZipEntry> e = zdir.entries(); e.hasMoreElements(); ) { + for (Enumeration<? extends ZipEntry> e = zfile.entries(); e.hasMoreElements(); ) { ZipEntry entry; try { entry = e.nextElement(); @@ -110,7 +112,7 @@ } public JavaFileObject getFileObject(RelativeDirectory subdirectory, String file) { - ZipEntry ze = new RelativeFile(subdirectory, file).getZipEntry(zdir); + ZipEntry ze = new RelativeFile(subdirectory, file).getZipEntry(zfile); return new ZipFileObject(this, file, ze); } @@ -119,17 +121,39 @@ } public void close() throws IOException { - zdir.close(); + zfile.close(); } @Override public String toString() { - return "ZipArchive[" + zdir.getName() + "]"; + return "ZipArchive[" + zfile.getName() + "]"; + } + + private File getAbsoluteFile() { + File absFile = (absFileRef == null ? null : absFileRef.get()); + if (absFile == null) { + absFile = new File(zfile.getName()).getAbsoluteFile(); + absFileRef = new SoftReference<File>(absFile); + } + return absFile; } + /** + * The file manager that created this archive. + */ protected JavacFileManager fileManager; + /** + * The index for the contents of this archive. + */ protected final Map<RelativeDirectory,List<String>> map; - protected final ZipFile zdir; + /** + * The zip file for the archive. + */ + protected final ZipFile zfile; + /** + * A reference to the absolute filename for the zip file for the archive. + */ + protected Reference<File> absFileRef; /** * A subclass of JavaFileObject representing zip entries. @@ -148,18 +172,18 @@ } public URI toUri() { - File zipFile = new File(zarch.zdir.getName()); + File zipFile = new File(zarch.zfile.getName()); return createJarUri(zipFile, entry.getName()); } @Override public String getName() { - return zarch.zdir.getName() + "(" + entry.getName() + ")"; + return zarch.zfile.getName() + "(" + entry.getName() + ")"; } @Override public String getShortName() { - return new File(zarch.zdir.getName()).getName() + "(" + entry + ")"; + return new File(zarch.zfile.getName()).getName() + "(" + entry + ")"; } @Override @@ -169,7 +193,7 @@ @Override public InputStream openInputStream() throws IOException { - return zarch.zdir.getInputStream(entry); + return zarch.zfile.getInputStream(entry); } @Override @@ -181,7 +205,7 @@ public CharBuffer getCharContent(boolean ignoreEncodingErrors) throws IOException { CharBuffer cb = fileManager.getCachedContent(this); if (cb == null) { - InputStream in = zarch.zdir.getInputStream(entry); + InputStream in = zarch.zfile.getInputStream(entry); try { ByteBuffer bb = fileManager.makeByteBuffer(in); JavaFileObject prev = fileManager.log.useSource(this); @@ -237,18 +261,27 @@ return name.equals(cn + k.extension); } + /** + * Check if two file objects are equal. + * Two ZipFileObjects are equal if the absolute paths of the underlying + * zip files are equal and if the paths within those zip files are equal. + */ @Override public boolean equals(Object other) { - if (!(other instanceof ZipFileObject)) { + if (this == other) + return true; + + if (!(other instanceof ZipFileObject)) return false; - } + ZipFileObject o = (ZipFileObject) other; - return zarch.zdir.equals(o.zarch.zdir) || name.equals(o.name); + return zarch.getAbsoluteFile().equals(o.zarch.getAbsoluteFile()) + && name.equals(o.name); } @Override public int hashCode() { - return zarch.zdir.hashCode() + name.hashCode(); + return zarch.getAbsoluteFile().hashCode() + name.hashCode(); } }
--- a/langtools/src/share/classes/com/sun/tools/javac/file/ZipFileIndex.java Fri Oct 23 18:44:33 2009 -0700 +++ b/langtools/src/share/classes/com/sun/tools/javac/file/ZipFileIndex.java Wed Oct 28 10:37:17 2009 -0700 @@ -30,6 +30,7 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.io.RandomAccessFile; +import java.lang.ref.Reference; import java.lang.ref.SoftReference; import java.util.ArrayList; import java.util.Arrays; @@ -89,6 +90,7 @@ // ZipFileIndex data entries private File zipFile; + private Reference<File> absFileRef; private long zipFileLastModified = NOT_MODIFIED; private RandomAccessFile zipRandomFile; private Entry[] entries; @@ -1215,6 +1217,15 @@ return zipFile; } + File getAbsoluteFile() { + File absFile = (absFileRef == null ? null : absFileRef.get()); + if (absFile == null) { + absFile = zipFile.getAbsoluteFile(); + absFileRef = new SoftReference<File>(absFile); + } + return absFile; + } + private RelativeDirectory getRelativeDirectory(String path) { RelativeDirectory rd; SoftReference<RelativeDirectory> ref = relativeDirectoryCache.get(path);
--- a/langtools/src/share/classes/com/sun/tools/javac/file/ZipFileIndexArchive.java Fri Oct 23 18:44:33 2009 -0700 +++ b/langtools/src/share/classes/com/sun/tools/javac/file/ZipFileIndexArchive.java Wed Oct 28 10:37:17 2009 -0700 @@ -219,17 +219,27 @@ return name.equals(cn + k.extension); } + /** + * Check if two file objects are equal. + * Two ZipFileIndexFileObjects are equal if the absolute paths of the underlying + * zip files are equal and if the paths within those zip files are equal. + */ @Override public boolean equals(Object other) { + if (this == other) + return true; + if (!(other instanceof ZipFileIndexFileObject)) return false; + ZipFileIndexFileObject o = (ZipFileIndexFileObject) other; - return entry.equals(o.entry); + return zfIndex.getAbsoluteFile().equals(o.zfIndex.getAbsoluteFile()) + && name.equals(o.name); } @Override public int hashCode() { - return zipName.hashCode() + (name.hashCode() << 10); + return zfIndex.getAbsoluteFile().hashCode() + name.hashCode(); } private String getPrefixedEntryName() {
--- a/langtools/src/share/classes/com/sun/tools/javac/jvm/ClassReader.java Fri Oct 23 18:44:33 2009 -0700 +++ b/langtools/src/share/classes/com/sun/tools/javac/jvm/ClassReader.java Wed Oct 28 10:37:17 2009 -0700 @@ -29,6 +29,7 @@ import java.net.URI; import java.net.URISyntaxException; import java.nio.CharBuffer; +import java.util.Arrays; import java.util.EnumSet; import java.util.HashMap; import java.util.Map; @@ -191,6 +192,16 @@ */ boolean debugJSR308; + /** A table to hold the constant pool indices for method parameter + * names, as given in LocalVariableTable attributes. + */ + int[] parameterNameIndices; + + /** + * Whether or not any parameter names have been found. + */ + boolean haveParameterNameIndices; + /** Get the ClassReader instance for this invocation. */ public static ClassReader instance(Context context) { ClassReader instance = context.get(classReaderKey); @@ -922,32 +933,33 @@ void read(Symbol sym, int attrLen) { int newbp = bp + attrLen; if (saveParameterNames) { - // pick up parameter names from the variable table - List<Name> parameterNames = List.nil(); - int firstParam = ((sym.flags() & STATIC) == 0) ? 1 : 0; - int endParam = firstParam + Code.width(sym.type.getParameterTypes()); + // Pick up parameter names from the variable table. + // Parameter names are not explicitly identified as such, + // but all parameter name entries in the LocalVariableTable + // have a start_pc of 0. Therefore, we record the name + // indicies of all slots with a start_pc of zero in the + // parameterNameIndicies array. + // Note that this implicitly honors the JVMS spec that + // there may be more than one LocalVariableTable, and that + // there is no specified ordering for the entries. int numEntries = nextChar(); - for (int i=0; i<numEntries; i++) { + for (int i = 0; i < numEntries; i++) { int start_pc = nextChar(); int length = nextChar(); int nameIndex = nextChar(); int sigIndex = nextChar(); int register = nextChar(); - if (start_pc == 0 && - firstParam <= register && - register < endParam) { - int index = firstParam; - for (Type t : sym.type.getParameterTypes()) { - if (index == register) { - parameterNames = parameterNames.prepend(readName(nameIndex)); - break; - } - index += Code.width(t); + if (start_pc == 0) { + // ensure array large enough + if (register >= parameterNameIndices.length) { + int newSize = Math.max(register, parameterNameIndices.length + 8); + parameterNameIndices = + Arrays.copyOf(parameterNameIndices, newSize); } + parameterNameIndices[register] = nameIndex; + haveParameterNameIndices = true; } } - parameterNames = parameterNames.reverse(); - ((MethodSymbol)sym).savedParameterNames = parameterNames; } bp = newbp; } @@ -1839,6 +1851,8 @@ syms.methodClass); } MethodSymbol m = new MethodSymbol(flags, name, type, currentOwner); + if (saveParameterNames) + initParameterNames(m); Symbol prevOwner = currentOwner; currentOwner = m; try { @@ -1846,9 +1860,90 @@ } finally { currentOwner = prevOwner; } + if (saveParameterNames) + setParameterNames(m, type); return m; } + /** + * Init the parameter names array. + * Parameter names are currently inferred from the names in the + * LocalVariableTable attributes of a Code attribute. + * (Note: this means parameter names are currently not available for + * methods without a Code attribute.) + * This method initializes an array in which to store the name indexes + * of parameter names found in LocalVariableTable attributes. It is + * slightly supersized to allow for additional slots with a start_pc of 0. + */ + void initParameterNames(MethodSymbol sym) { + // make allowance for synthetic parameters. + final int excessSlots = 4; + int expectedParameterSlots = + Code.width(sym.type.getParameterTypes()) + excessSlots; + if (parameterNameIndices == null + || parameterNameIndices.length < expectedParameterSlots) { + parameterNameIndices = new int[expectedParameterSlots]; + } else + Arrays.fill(parameterNameIndices, 0); + haveParameterNameIndices = false; + } + + /** + * Set the parameter names for a symbol from the name index in the + * parameterNameIndicies array. The type of the symbol may have changed + * while reading the method attributes (see the Signature attribute). + * This may be because of generic information or because anonymous + * synthetic parameters were added. The original type (as read from + * the method descriptor) is used to help guess the existence of + * anonymous synthetic parameters. + * On completion, sym.savedParameter names will either be null (if + * no parameter names were found in the class file) or will be set to a + * list of names, one per entry in sym.type.getParameterTypes, with + * any missing names represented by the empty name. + */ + void setParameterNames(MethodSymbol sym, Type jvmType) { + // if no names were found in the class file, there's nothing more to do + if (!haveParameterNameIndices) + return; + + int firstParam = ((sym.flags() & STATIC) == 0) ? 1 : 0; + // the code in readMethod may have skipped the first parameter when + // setting up the MethodType. If so, we make a corresponding allowance + // here for the position of the first parameter. Note that this + // assumes the skipped parameter has a width of 1 -- i.e. it is not + // a double width type (long or double.) + if (sym.name == names.init && currentOwner.hasOuterInstance()) { + // Sometimes anonymous classes don't have an outer + // instance, however, there is no reliable way to tell so + // we never strip this$n + if (!currentOwner.name.isEmpty()) + firstParam += 1; + } + + if (sym.type != jvmType) { + // reading the method attributes has caused the symbol's type to + // be changed. (i.e. the Signature attribute.) This may happen if + // there are hidden (synthetic) parameters in the descriptor, but + // not in the Signature. The position of these hidden parameters + // is unspecified; for now, assume they are at the beginning, and + // so skip over them. The primary case for this is two hidden + // parameters passed into Enum constructors. + int skip = Code.width(jvmType.getParameterTypes()) + - Code.width(sym.type.getParameterTypes()); + firstParam += skip; + } + List<Name> paramNames = List.nil(); + int index = firstParam; + for (Type t: sym.type.getParameterTypes()) { + int nameIdx = (index < parameterNameIndices.length + ? parameterNameIndices[index] : 0); + Name name = nameIdx == 0 ? names.empty : readName(nameIdx); + paramNames = paramNames.prepend(name); + index += Code.width(t); + } + sym.savedParameterNames = paramNames.reverse(); + } + /** Skip a field or method */ void skipMember() { @@ -2632,10 +2727,20 @@ return true; // fail-safe mode } + /** + * Check if two file objects are equal. + * SourceFileObjects are just placeholder objects for the value of a + * SourceFile attribute, and do not directly represent specific files. + * Two SourceFileObjects are equal if their names are equal. + */ @Override public boolean equals(Object other) { + if (this == other) + return true; + if (!(other instanceof SourceFileObject)) return false; + SourceFileObject o = (SourceFileObject) other; return name.equals(o.name); }
--- a/langtools/src/share/classes/com/sun/tools/javac/parser/Scanner.java Fri Oct 23 18:44:33 2009 -0700 +++ b/langtools/src/share/classes/com/sun/tools/javac/parser/Scanner.java Wed Oct 28 10:37:17 2009 -0700 @@ -876,7 +876,11 @@ } scanChar(); skipIllegalUnderscores(); - scanNumber(2); + if (digit(2) < 0) { + lexError("invalid.binary.number"); + } else { + scanNumber(2); + } } else { putChar('0'); if (ch == '_') {
--- a/langtools/src/share/classes/com/sun/tools/javac/resources/compiler.properties Fri Oct 23 18:44:33 2009 -0700 +++ b/langtools/src/share/classes/com/sun/tools/javac/resources/compiler.properties Wed Oct 28 10:37:17 2009 -0700 @@ -252,6 +252,8 @@ interface methods cannot have body compiler.err.invalid.annotation.member.type=\ invalid type for annotation member +compiler.err.invalid.binary.number=\ + binary numbers must contain at least one binary digit compiler.err.invalid.hex.number=\ hexadecimal numbers must contain at least one hexadecimal digit compiler.err.invalid.meth.decl.ret.type.req=\
--- a/langtools/src/share/classes/com/sun/tools/javap/ClassWriter.java Fri Oct 23 18:44:33 2009 -0700 +++ b/langtools/src/share/classes/com/sun/tools/javap/ClassWriter.java Wed Oct 28 10:37:17 2009 -0700 @@ -179,10 +179,10 @@ // The signature parser cannot disambiguate between a // FieldType and a ClassSignatureType that only contains a superclass type. if (t instanceof Type.ClassSigType) - print(t); + print(getJavaName(t.toString())); else { print(" extends "); - print(t); + print(getJavaName(t.toString())); } } catch (ConstantPoolException e) { print(report(e)); @@ -310,7 +310,7 @@ writeModifiers(flags.getMethodModifiers()); if (methodType != null) { - writeListIfNotEmpty("<", methodType.typeArgTypes, "> "); + writeListIfNotEmpty("<", methodType.typeParamTypes, "> "); } if (getName(m).equals("<init>")) { print(getJavaName(classFile));
--- a/langtools/src/share/classes/com/sun/tools/javap/LocalVariableTypeTableWriter.java Fri Oct 23 18:44:33 2009 -0700 +++ b/langtools/src/share/classes/com/sun/tools/javap/LocalVariableTypeTableWriter.java Wed Oct 28 10:37:17 2009 -0700 @@ -125,7 +125,7 @@ print(" // "); Descriptor d = new Signature(entry.signature_index); try { - print(d.getFieldType(constant_pool)); + print(d.getFieldType(constant_pool).toString().replace("/", ".")); } catch (InvalidDescriptor e) { print(report(e)); } catch (ConstantPoolException e) {
--- a/langtools/src/share/classes/javax/lang/model/util/Elements.java Fri Oct 23 18:44:33 2009 -0700 +++ b/langtools/src/share/classes/javax/lang/model/util/Elements.java Wed Oct 28 10:37:17 2009 -0700 @@ -77,9 +77,25 @@ * Returns the text of the documentation ("Javadoc") * comment of an element. * + * <p> A documentation comment of an element is a comment that + * begins with "{@code /**}" , ends with a separate + * "<code>*/</code>", and immediately precedes the element, + * ignoring white space. Therefore, a documentation comment + * contains at least three"{@code *}" characters. The text + * returned for the documentation comment is a processed form of + * the comment as it appears in source code. The leading "{@code + * /**}" and trailing "<code>*/</code>" are removed. For lines + * of the comment starting after the initial "{@code /**}", + * leading white space characters are discarded as are any + * consecutive "{@code *}" characters appearing after the white + * space or starting the line. The processed lines are then + * concatenated together (including line terminators) and + * returned. + * * @param e the element being examined * @return the documentation comment of the element, or {@code null} * if there is none + * @jls3 3.6 White Space */ String getDocComment(Element e);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/langtools/test/tools/javac/6889255/T6889255.java Wed Oct 28 10:37:17 2009 -0700 @@ -0,0 +1,485 @@ +/* + * 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 6889255 + * @summary ClassReader does not read parameter names correctly + */ + +import java.io.*; +import java.util.*; +import javax.tools.StandardLocation; +import com.sun.tools.javac.code.Flags; +import com.sun.tools.javac.code.Kinds; +import com.sun.tools.javac.code.Scope; +import com.sun.tools.javac.code.Symbol.*; +import com.sun.tools.javac.code.Type; +import com.sun.tools.javac.code.Type.ClassType; +import com.sun.tools.javac.code.TypeTags; +import com.sun.tools.javac.file.JavacFileManager; +import com.sun.tools.javac.jvm.ClassReader; +import com.sun.tools.javac.util.Context; +import com.sun.tools.javac.util.Names; + +public class T6889255 { + boolean testInterfaces = true; + boolean testSyntheticMethods = true; + + // The following enums control the generation of the test methods to be compiled. + enum GenericKind { + NOT_GENERIC, + GENERIC + }; + + enum ClassKind { + CLASS("Clss"), + INTERFACE("Intf"), + ENUM("Enum"); + final String base; + ClassKind(String base) { this.base = base; } + }; + + enum NestedKind { + /** Declare methods inside the outermost container. */ + NONE, + /** Declare methods inside a container with a 'static' modifier. */ + NESTED, + /** Declare methods inside a container without a 'static' modifier. */ + INNER, + /** Declare methods inside a local class in an initializer. */ + INIT_LOCAL, + /** Declare methods inside an anonymous class in an initializer. */ + INIT_ANON, + /** Declare methods inside a local class in a method. */ + METHOD_LOCAL, + /** Declare methods inside an anonymous class in a method. */ + METHOD_ANON + }; + + enum MethodKind { + ABSTRACT, + CONSTRUCTOR, + METHOD, + STATIC_METHOD, + BRIDGE_METHOD + }; + + enum FinalKind { + /** Method body does not reference external final variables. */ + NO_FINAL, + /** Method body references external final variables. */ + USE_FINAL + }; + + public static void main(String... args) throws Exception { + new T6889255().run(); + } + + void run() throws Exception { + genTest(); + + test("no-args", false); + test("g", true, "-g"); + + if (errors > 0) + throw new Exception(errors + " errors found"); + } + + /** + * Create a file containing lots of method definitions to be tested. + * There are 3 sets of nested loops that generate the methods. + * 1. The outermost set declares [generic] (class | interface | enum) + * 2. The middle set declares [(nested | inner | anon | local)] class + * 3. The innermost set declares + * [generic] (constructor|method|static-method|bridge-method) [using final variables in outer scope] + * Invalid combinations are filtered out. + */ + void genTest() throws Exception { + BufferedWriter out = new BufferedWriter(new FileWriter("Test.java")); + + // This interface is used to force bridge methods to be generated, by + // implementing its methods with subtypes of Object + out.write("interface Base {\n"); + out.write(" Object base_m1(int i1);\n"); + out.write(" Object base_m2(int i1);\n"); + out.write("}\n"); + + int outerNum = 0; + // Outermost set of loops, to generate a top level container + for (GenericKind outerGenericKind: GenericKind.values()) { + for (ClassKind outerClassKind: ClassKind.values()) { + if (outerGenericKind == GenericKind.GENERIC && outerClassKind == ClassKind.ENUM) + continue; + String outerClassName = outerClassKind.base + (outerNum++); + String outerTypeArg = outerClassKind.toString().charAt(0) + "T"; + if (outerClassKind == ClassKind.CLASS) + out.write("abstract "); + out.write(outerClassKind.toString().toLowerCase() + " " + outerClassName); + if (outerGenericKind == GenericKind.GENERIC) + out.write("<" + outerTypeArg + ">"); + if (outerClassKind == ClassKind.INTERFACE) + out.write(" extends Base"); + else + out.write(" implements Base"); + out.write(" {\n"); + if (outerClassKind == ClassKind.ENUM) { + out.write(" E1(0,0,0), E2(0,0,0), E3(0,0,0);\n"); + out.write(" " + outerClassName + "(int i1, int i2, int i3) { }\n"); + } + // Middle set of loops, to generate an optional nested container + int nestedNum = 0; + int methodNum = 0; + for (GenericKind nestedGenericKind: GenericKind.values()) { + nextNestedKind: + for (NestedKind nestedKind: NestedKind.values()) { + // if the nested kind is none, there is no point iterating over all + // nested generic kinds, so arbitarily limit it to just one kind + if (nestedKind == NestedKind.NONE && nestedGenericKind != GenericKind.NOT_GENERIC) + continue; + if ((nestedKind == NestedKind.METHOD_ANON || nestedKind == NestedKind.INIT_ANON) + && nestedGenericKind == GenericKind.GENERIC) + continue; + String indent = " "; + boolean haveFinal = false; + switch (nestedKind) { + case METHOD_ANON: case METHOD_LOCAL: + if (outerClassKind == ClassKind.INTERFACE) + continue nextNestedKind; + out.write(indent + "void m" + + (nestedNum++) + "() {\n"); + indent += " "; + out.write(indent + "final int fi1 = 0;\n"); + haveFinal = true; + break; + case INIT_ANON: case INIT_LOCAL: + if (outerClassKind == ClassKind.INTERFACE) + continue nextNestedKind; + out.write(indent + "{\n"); + indent += " "; + break; + } + for (ClassKind nestedClassKind: ClassKind.values()) { + if ((nestedGenericKind == GenericKind.GENERIC) + && (nestedClassKind == ClassKind.ENUM)) + continue; + if ((nestedKind == NestedKind.METHOD_ANON || nestedKind == NestedKind.METHOD_LOCAL + || nestedKind == NestedKind.INIT_ANON || nestedKind == NestedKind.INIT_LOCAL) + && nestedClassKind != ClassKind.CLASS) + continue; + // if the nested kind is none, there is no point iterating over all + // nested class kinds, so arbitarily limit it to just one kind + if (nestedKind == NestedKind.NONE && nestedClassKind != ClassKind.CLASS) + continue; + + ClassKind methodClassKind; + String methodClassName; + boolean allowAbstractMethods; + boolean allowStaticMethods; + switch (nestedKind) { + case NONE: + methodClassKind = outerClassKind; + methodClassName = outerClassName; + allowAbstractMethods = (outerClassKind == ClassKind.CLASS); + allowStaticMethods = (outerClassKind != ClassKind.INTERFACE); + break; + case METHOD_ANON: + case INIT_ANON: + out.write(indent + "new Base() {\n"); + indent += " "; + methodClassKind = ClassKind.CLASS; + methodClassName = null; + allowAbstractMethods = false; + allowStaticMethods = false; + break; + default: { // INNER, NESTED, LOCAL + String nestedClassName = "N" + nestedClassKind.base + (nestedNum++); + String nestedTypeArg = nestedClassKind.toString().charAt(0) + "T"; + out.write(indent); + if (nestedKind == NestedKind.NESTED) + out.write("static "); + if (nestedClassKind == ClassKind.CLASS) + out.write("abstract "); + out.write(nestedClassKind.toString().toLowerCase() + " " + nestedClassName); + if (nestedGenericKind == GenericKind.GENERIC) + out.write("<" + nestedTypeArg + ">"); + if (nestedClassKind == ClassKind.INTERFACE) + out.write(" extends Base "); + else + out.write(" implements Base "); + out.write(" {\n"); + indent += " "; + if (nestedClassKind == ClassKind.ENUM) { + out.write(indent + "E1(0,0,0), E2(0,0,0), E3(0,0,0);\n"); + out.write(indent + nestedClassName + "(int i1, int i2, int i3) { }\n"); + } + methodClassKind = nestedClassKind; + methodClassName = nestedClassName; + allowAbstractMethods = (nestedClassKind == ClassKind.CLASS); + allowStaticMethods = (nestedKind == NestedKind.NESTED && nestedClassKind != ClassKind.INTERFACE); + break; + } + } + + // Innermost loops, to generate methods + for (GenericKind methodGenericKind: GenericKind.values()) { + for (FinalKind finalKind: FinalKind.values()) { + for (MethodKind methodKind: MethodKind.values()) { +// out.write("// " + outerGenericKind +// + " " + outerClassKind +// + " " + nestedKind +// + " " + nestedGenericKind +// + " " + nestedClassKind +// + " " + methodGenericKind +// + " " + finalKind +// + " " + methodKind +// + "\n"); + switch (methodKind) { + case CONSTRUCTOR: + if (nestedKind == NestedKind.METHOD_ANON || nestedKind == NestedKind.INIT_ANON) + break; + if (methodClassKind != ClassKind.CLASS) + break; + if (finalKind == FinalKind.USE_FINAL && !haveFinal) + break; + out.write(indent); + if (methodGenericKind == GenericKind.GENERIC) { + out.write("<CT> " + methodClassName + "(CT c1, CT c2"); + } else { + out.write(methodClassName + "(boolean b1, char c2"); + } + if (finalKind == FinalKind.USE_FINAL) { + // add a dummy parameter to avoid duplicate declaration + out.write(", int i3) { int i = fi1; }\n"); + } else + out.write(") { }\n"); + break; + case ABSTRACT: + if (!allowAbstractMethods) + continue; + // fallthrough + case METHOD: + if (finalKind == FinalKind.USE_FINAL && !haveFinal) + break; + out.write(indent); + if (methodKind == MethodKind.ABSTRACT) + out.write("abstract "); + if (methodGenericKind == GenericKind.GENERIC) + out.write("<MT> "); + out.write("void m" + (methodNum++) + "(int i1, long l2, float f3)"); + if (methodKind == MethodKind.ABSTRACT || methodClassKind == ClassKind.INTERFACE) + out.write(";\n"); + else { + out.write(" {"); + if (finalKind == FinalKind.USE_FINAL) + out.write(" int i = fi1;"); + out.write(" }\n"); + } + break; + case BRIDGE_METHOD: + if (methodGenericKind == GenericKind.GENERIC) + break; + out.write(indent); + // methods Base.base_m1 and Base.base_m2 are declared for the + // benefit of bridge methods. They need to be implemented + // whether or not a final variable is used. + String methodName = (finalKind == FinalKind.NO_FINAL ? "base_m1" : "base_m2"); + out.write("public String " + methodName + "(int i1)"); + if (methodClassKind == ClassKind.INTERFACE) + out.write(";\n"); + else { + out.write(" {"); + if (finalKind == FinalKind.USE_FINAL && haveFinal) + out.write(" int i = fi1;"); + out.write(" return null; }\n"); + } + break; + case STATIC_METHOD: + if (!allowStaticMethods) + break; + if (finalKind == FinalKind.USE_FINAL && !haveFinal) + break; + out.write(indent + "static "); + if (methodGenericKind == GenericKind.GENERIC) + out.write("<MT> "); + out.write("void m" + (methodNum++) + "(int i1, long l2, float f3) {"); + if (finalKind == FinalKind.USE_FINAL) + out.write(" int i = fi1;"); + out.write(" }\n"); + break; + } + + } + } + } + if (nestedKind != NestedKind.NONE) { + indent = indent.substring(0, indent.length() - 4); + out.write(indent + "};\n"); + } + } + switch (nestedKind) { + case METHOD_ANON: case METHOD_LOCAL: + case INIT_ANON: case INIT_LOCAL: + indent = indent.substring(0, indent.length() - 4); + out.write(indent + "}\n\n"); + } + } + } + out.write("}\n\n"); + } + } + out.close(); + } + + + void test(String testName, boolean expectNames, String... opts) throws Exception { + System.err.println("Test " + testName + + ": expectNames:" + expectNames + + " javacOpts:" + Arrays.asList(opts)); + + File outDir = new File(testName); + outDir.mkdirs(); + compile(outDir, opts); + + Context ctx = new Context(); + JavacFileManager fm = new JavacFileManager(ctx, true, null); + fm.setLocation(StandardLocation.CLASS_PATH, Arrays.asList(outDir)); + ClassReader cr = ClassReader.instance(ctx); + cr.saveParameterNames = true; + Names names = Names.instance(ctx); + + Set<String> classes = getTopLevelClasses(outDir); + Deque<String> work = new LinkedList<String>(classes); + String classname; + while ((classname = work.poll()) != null) { + System.err.println("Checking class " + classname); + ClassSymbol sym = cr.enterClass(names.table.fromString(classname)); + sym.complete(); + + if ((sym.flags() & Flags.INTERFACE) != 0 && !testInterfaces) + continue; + + for (Scope.Entry e = sym.members_field.elems; e != null; e = e.sibling) { + System.err.println("Checking member " + e.sym); + switch (e.sym.kind) { + case Kinds.TYP: { + String name = e.sym.flatName().toString(); + if (!classes.contains(name)) { + classes.add(name); + work.add(name); + } + break; + } + case Kinds.MTH: + verify((MethodSymbol) e.sym, expectNames); + break; + } + + } + } + } + + void verify(MethodSymbol m, boolean expectNames) { + if ((m.flags() & Flags.SYNTHETIC) != 0 && !testSyntheticMethods) + return; + + //System.err.println("verify: " + m.params()); + int i = 1; + for (VarSymbol v: m.params()) { + String expectName; + if (expectNames) + expectName = getExpectedName(v, i); + else + expectName = "arg" + (i - 1); + checkEqual(expectName, v.name.toString()); + i++; + } + } + + String getExpectedName(VarSymbol v, int i) { + // special cases: + // synthetic method + if (((v.owner.owner.flags() & Flags.ENUM) != 0) + && v.owner.name.toString().equals("valueOf")) + return "name"; + // interfaces don't have saved names + // -- no Code attribute for the LocalVariableTable attribute + if ((v.owner.owner.flags() & Flags.INTERFACE) != 0) + return "arg" + (i - 1); + // abstract methods don't have saved names + // -- no Code attribute for the LocalVariableTable attribute + if ((v.owner.flags() & Flags.ABSTRACT) != 0) + return "arg" + (i - 1); + // bridge methods use xN + if ((v.owner.flags() & Flags.BRIDGE) != 0) + return "x" + (i - 1); + + // The rest of this method assumes the local conventions in the test program + Type t = v.type; + String s; + if (t.tag == TypeTags.CLASS) + s = ((ClassType) t).tsym.name.toString(); + else + s = t.toString(); + return String.valueOf(Character.toLowerCase(s.charAt(0))) + i; + } + + void compile(File outDir, String... opts) throws Exception { + //File testSrc = new File(System.getProperty("test.src"), "."); + List<String> args = new ArrayList<String>(); + args.add("-d"); + args.add(outDir.getPath()); + args.addAll(Arrays.asList(opts)); + //args.add(new File(testSrc, "Test.java").getPath()); + args.add("Test.java"); + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + int rc = com.sun.tools.javac.Main.compile(args.toArray(new String[args.size()]), pw); + pw.close(); + if (rc != 0) { + System.err.println(sw.toString()); + throw new Exception("compilation failed unexpectedly"); + } + } + + Set<String> getTopLevelClasses(File outDir) { + Set<String> classes = new HashSet<String>(); + for (String f: outDir.list()) { + if (f.endsWith(".class") && !f.contains("$")) + classes.add(f.replace(".class", "")); + } + return classes; + } + + void checkEqual(String expect, String found) { + if (!expect.equals(found)) + error("mismatch: expected:" + expect + " found:" + found); + } + + void error(String msg) { + System.err.println(msg); + errors++; + throw new Error(); + } + + int errors; +}
--- a/langtools/test/tools/javac/api/6440528/T6440528.java Fri Oct 23 18:44:33 2009 -0700 +++ b/langtools/test/tools/javac/api/6440528/T6440528.java Wed Oct 28 10:37:17 2009 -0700 @@ -59,9 +59,9 @@ } private File getUnderlyingFile(Object o) throws Exception { - Field f = o.getClass().getDeclaredField("f"); - f.setAccessible(true); - return (File)f.get(o); + Field file = o.getClass().getDeclaredField("file"); + file.setAccessible(true); + return (File)file.get(o); } public static void main(String... args) throws Exception {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/langtools/test/tools/javac/api/T6838467.java Wed Oct 28 10:37:17 2009 -0700 @@ -0,0 +1,249 @@ +/* + * 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 6838467 + * @summary JSR199 FileObjects don't obey general contract of equals. + */ + +import java.io.*; +import java.util.*; +import java.util.zip.*; +import javax.tools.*; +import com.sun.tools.javac.file.JavacFileManager; +import com.sun.tools.javac.util.Context; + +public class T6838467 { + boolean fileSystemIsCaseSignificant = !new File("a").equals(new File("A")); + + enum FileKind { + DIR("dir"), + ZIP("zip"), + ZIPFILEINDEX("zip"); + FileKind(String path) { + file = new File(path); + } + final File file; + }; + + enum CompareKind { + SAME { + File other(File f) { return f; } + }, + ABSOLUTE { + File other(File f) { return f.getAbsoluteFile(); } + }, + DIFFERENT { + File other(File f) { return new File("not_" + f.getPath()); } + }, + CASEEQUIV { + File other(File f) { return new File(f.getPath().toUpperCase()); } + }; + abstract File other(File f); + }; + + String[] paths = { "p/A.java", "p/B.java", "p/C.java" }; + + public static void main(String... args) throws Exception { + new T6838467().run(); + } + + void run() throws Exception { + // on Windows, verify file system is not case significant + if (System.getProperty("os.name").toLowerCase().startsWith("windows") + && fileSystemIsCaseSignificant) { + error("fileSystemIsCaseSignificant is set on Windows."); + } + + // create a set of directories and zip files to compare + createTestDir(new File("dir"), paths); + createTestDir(new File("not_dir"), paths); + createTestZip(new File("zip"), paths); + createTestZip(new File("not_zip"), paths); + if (fileSystemIsCaseSignificant) { + createTestDir(new File("DIR"), paths); + createTestZip(new File("ZIP"), paths); + } + + // test the various sorts of file objects that can be obtained from + // the file manager, and for various values that may or may not match. + for (FileKind fk: FileKind.values()) { + for (CompareKind ck: CompareKind.values()) { + test(fk, ck); + } + } + + // verify that the various different types of file object were all + // tested + Set<String> expectClasses = new HashSet<String>(Arrays.asList( + "RegularFileObject", "ZipFileObject", "ZipFileIndexFileObject" )); + if (!foundClasses.equals(expectClasses)) { + error("expected fileobject classes not found\n" + + "expected: " + expectClasses + "\n" + + "found: " + foundClasses); + } + + if (errors > 0) + throw new Exception(errors + " errors"); + } + + void test(FileKind fk, CompareKind ck) throws IOException { + File f1 = fk.file; + JavaFileManager fm1 = createFileManager(fk, f1); + + File f2 = ck.other(fk.file); + JavaFileManager fm2 = createFileManager(fk, f2); + + try { + // If the directories or zip files match, we expect "n" matches in + // the "n-squared" comparisons to come, where "n" is the number of + // entries in the the directories or zip files. + // If the directories or zip files don't themselves match, + // we obviously don't expect any of their contents to match either. + int expect = (f1.getAbsoluteFile().equals(f2.getAbsoluteFile()) ? paths.length : 0); + + System.err.println("test " + (++count) + " " + fk + " " + ck + " " + f1 + " " + f2); + test(fm1, fm2, expect); + + } finally { + fm1.close(); + fm2.close(); + } + } + + // For a pair of file managers that may or may not have similar entries + // on the classpath, compare all files returned from one against all files + // returned from the other. For each pair of files, verify that if they + // are equal, the hashcode is equal as well, and finally verify that the + // expected number of matches was found. + void test(JavaFileManager fm1, JavaFileManager fm2, int expectEqualCount) throws IOException { + boolean foundFiles1 = false; + boolean foundFiles2 = false; + int foundEqualCount = 0; + Set<JavaFileObject.Kind> kinds = EnumSet.allOf(JavaFileObject.Kind.class); + for (FileObject fo1: fm1.list(StandardLocation.CLASS_PATH, "p", kinds, false)) { + foundFiles1 = true; + foundClasses.add(fo1.getClass().getSimpleName()); + for (FileObject fo2: fm2.list(StandardLocation.CLASS_PATH, "p", kinds, false)) { + foundFiles2 = true; + foundClasses.add(fo1.getClass().getSimpleName()); + System.err.println("compare " + fo1 + " " + fo2); + if (fo1.equals(fo2)) { + foundEqualCount++; + int hash1 = fo1.hashCode(); + int hash2 = fo2.hashCode(); + if (hash1 != hash2) + error("hashCode error: " + fo1 + " [" + hash1 + "] " + + fo2 + " [" + hash2 + "]"); + } + } + } + if (!foundFiles1) + error("no files found for file manager 1"); + if (!foundFiles2) + error("no files found for file manager 2"); + // verify the expected number of matches were found + if (foundEqualCount != expectEqualCount) + error("expected matches not found: expected " + expectEqualCount + ", found " + foundEqualCount); + } + + // create a file manager to test a FileKind, with a given directory + // or zip file placed on the classpath + JavaFileManager createFileManager(FileKind fk, File classpath) throws IOException { + StandardJavaFileManager fm = createFileManager(fk == FileKind.ZIP); + fm.setLocation(StandardLocation.CLASS_PATH, Arrays.asList(classpath)); + return fm; + } + + JavacFileManager createFileManager(boolean useJavaUtilZip) { + // javac should really not be using system properties like this + // -- it should really be using (hidden) options -- but until then + // take care to leave system properties as we find them, so as not + // to adversely affect other tests that might follow. + String prev = System.getProperty("useJavaUtilZip"); + boolean resetProperties = false; + try { + if (useJavaUtilZip) { + System.setProperty("useJavaUtilZip", "true"); + resetProperties = true; + } else if (System.getProperty("useJavaUtilZip") != null) { + System.getProperties().remove("useJavaUtilZip"); + resetProperties = true; + } + + Context c = new Context(); + return new JavacFileManager(c, false, null); + } finally { + if (resetProperties) { + if (prev == null) { + System.getProperties().remove("useJavaUtilZip"); + } else { + System.setProperty("useJavaUtilZip", prev); + } + } + } + } + + // create a directory containing a given set of paths + void createTestDir(File dir, String[] paths) throws IOException { + for (String p: paths) { + File file = new File(dir, p); + file.getParentFile().mkdirs(); + FileWriter out = new FileWriter(file); + try { + out.write(p); + } finally { + out.close(); + } + } + } + + // create a sip file containing a given set of entries + void createTestZip(File zip, String[] paths) throws IOException { + if (zip.getParentFile() != null) + zip.getParentFile().mkdirs(); + ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(zip)); + try { + for (String p: paths) { + ZipEntry ze = new ZipEntry(p); + zos.putNextEntry(ze); + byte[] bytes = p.getBytes(); + zos.write(bytes, 0, bytes.length); + zos.closeEntry(); + } + } finally { + zos.close(); + } + } + + void error(String msg) { + System.err.println("Error: " + msg); + errors++; + } + + int count; + int errors; + Set<String> foundClasses = new HashSet<String>(); +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/langtools/test/tools/javac/literals/T6891079.java Wed Oct 28 10:37:17 2009 -0700 @@ -0,0 +1,12 @@ +/* @test /nodynamiccopyright/ + * @bug 6891079 + * @summary Compiler allows invalid binary literals 0b and oBL + * @compile/fail/ref=T6891079.out -XDrawDiagnostics T6891079.java + */ + +class Test { + int bi = 0B; + long bl = 0BL; + int xi = 0X; + long xl = 0XL; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/langtools/test/tools/javac/literals/T6891079.out Wed Oct 28 10:37:17 2009 -0700 @@ -0,0 +1,7 @@ +T6891079.java:8:14: compiler.err.invalid.binary.number +T6891079.java:9:15: compiler.err.invalid.binary.number +T6891079.java:9:18: compiler.err.expected: token.identifier +T6891079.java:10:14: compiler.err.invalid.hex.number +T6891079.java:11:15: compiler.err.invalid.hex.number +T6891079.java:11:18: compiler.err.expected: token.identifier +6 errors
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/langtools/test/tools/javap/classfile/6888367/T6888367.java Wed Oct 28 10:37:17 2009 -0700 @@ -0,0 +1,511 @@ +/* + * 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. + */ + +import java.io.*; +import java.net.*; +import java.util.*; +import com.sun.tools.classfile.*; +import com.sun.tools.classfile.Type.ArrayType; +import com.sun.tools.classfile.Type.ClassSigType; +import com.sun.tools.classfile.Type.ClassType; +import com.sun.tools.classfile.Type.MethodType; +import com.sun.tools.classfile.Type.SimpleType; +import com.sun.tools.classfile.Type.TypeParamType; +import com.sun.tools.classfile.Type.WildcardType; + +/* + * @test + * @bug 6888367 + * @summary classfile library parses signature attributes incorrectly + */ + +/* + * This test is a pretty detailed test both of javac signature generation and classfile + * signature parsing. The first part of the test tests all the examples given in the + * second part of the test. Each example comes with one or two annotations, @Desc, @Sig, + * for the descriptor and signature of the annotated declaration. Annotations are + * provided whenever the annotated item is expected to have a corresponding value. + * Each annotation has two argument values. The first arg is the expected value of the + * descriptor/signature as found in the class file. This value is mostly for documentation + * purposes in reading the test. The second value is the rendering of the descriptor or + * signature using a custom Type visitor that explicitly includes an indication of the + * Type classes being used to represent the descriptor/signature. Thus we test + * that the descriptor/signature is being parsed into the expected type tree structure. + */ +public class T6888367 { + + public static void main(String... args) throws Exception { + new T6888367().run(); + } + + public void run() throws Exception { + ClassFile cf = getClassFile("Test"); + + testFields(cf); + testMethods(cf); + testInnerClasses(cf); // recursive + + if (errors > 0) + throw new Exception(errors + " errors found"); + } + + void testFields(ClassFile cf) throws Exception { + String cn = cf.getName(); + ConstantPool cp = cf.constant_pool; + for (Field f: cf.fields) { + test("field " + cn + "." + f.getName(cp), f.descriptor, f.attributes, cp); + } + } + + void testMethods(ClassFile cf) throws Exception { + String cn = cf.getName(); + ConstantPool cp = cf.constant_pool; + for (Method m: cf.methods) { + test("method " + cn + "." + m.getName(cp), m.descriptor, m.attributes, cp); + } + } + + void testInnerClasses(ClassFile cf) throws Exception { + ConstantPool cp = cf.constant_pool; + InnerClasses_attribute ic = + (InnerClasses_attribute) cf.attributes.get(Attribute.InnerClasses); + for (InnerClasses_attribute.Info info: ic.classes) { + String outerClassName = cp.getClassInfo(info.outer_class_info_index).getName(); + if (!outerClassName.equals(cf.getName())) { + continue; + } + String innerClassName = cp.getClassInfo(info.inner_class_info_index).getName(); + ClassFile icf = getClassFile(innerClassName); + test("class " + innerClassName, null, icf.attributes, icf.constant_pool); + testInnerClasses(icf); + } + } + + void test(String name, Descriptor desc, Attributes attrs, ConstantPool cp) + throws Exception { + AnnotValues d = getDescValue(attrs, cp); + AnnotValues s = getSigValue(attrs, cp); + if (d == null && s == null) // not a test field or method if no @Desc or @Sig given + return; + + System.err.println(name); + + if (desc != null) { + System.err.println(" descriptor: " + desc.getValue(cp)); + checkEqual(d.raw, desc.getValue(cp)); + Type dt = new Signature(desc.index).getType(cp); + checkEqual(d.type, tp.print(dt)); + } + + Signature_attribute sa = (Signature_attribute) attrs.get(Attribute.Signature); + if (sa != null) + System.err.println(" signature: " + sa.getSignature(cp)); + + if (s != null || sa != null) { + if (s != null && sa != null) { + checkEqual(s.raw, sa.getSignature(cp)); + Type st = new Signature(sa.signature_index).getType(cp); + checkEqual(s.type, tp.print(st)); + } else if (s != null) + error("@Sig annotation found but not Signature attribute"); + else + error("Signature attribute found but no @Sig annotation"); + } + + System.err.println(); + } + + + ClassFile getClassFile(String name) throws IOException, ConstantPoolException { + URL url = getClass().getResource(name + ".class"); + InputStream in = url.openStream(); + try { + return ClassFile.read(in); + } finally { + in.close(); + } + } + + AnnotValues getDescValue(Attributes attrs, ConstantPool cp) throws Exception { + return getAnnotValues(Desc.class.getName(), attrs, cp); + } + + AnnotValues getSigValue(Attributes attrs, ConstantPool cp) throws Exception { + return getAnnotValues(Sig.class.getName(), attrs, cp); + } + + static class AnnotValues { + AnnotValues(String raw, String type) { + this.raw = raw; + this.type = type; + } + final String raw; + final String type; + } + + AnnotValues getAnnotValues(String annotName, Attributes attrs, ConstantPool cp) + throws Exception { + RuntimeInvisibleAnnotations_attribute annots = + (RuntimeInvisibleAnnotations_attribute)attrs.get(Attribute.RuntimeInvisibleAnnotations); + if (annots != null) { + for (Annotation a: annots.annotations) { + if (cp.getUTF8Value(a.type_index).equals("L" + annotName + ";")) { + Annotation.Primitive_element_value pv0 = + (Annotation.Primitive_element_value) a.element_value_pairs[0].value; + Annotation.Primitive_element_value pv1 = + (Annotation.Primitive_element_value) a.element_value_pairs[1].value; + return new AnnotValues( + cp.getUTF8Value(pv0.const_value_index), + cp.getUTF8Value(pv1.const_value_index)); + } + } + } + return null; + + } + + void checkEqual(String expect, String found) { + if (!(expect == null ? found == null : expect.equals(found))) { + System.err.println("expected: " + expect); + System.err.println(" found: " + found); + error("unexpected values found"); + } + } + + void error(String msg) { + System.err.println("error: " + msg); + errors++; + } + + int errors; + + TypePrinter tp = new TypePrinter(); + + class TypePrinter implements Type.Visitor<String,Void> { + String print(Type t) { + return t == null ? null : t.accept(this, null); + } + String print(String pre, List<? extends Type> ts, String post) { + if (ts == null) + return null; + StringBuilder sb = new StringBuilder(); + sb.append(pre); + String sep = ""; + for (Type t: ts) { + sb.append(sep); + sb.append(print(t)); + sep = ","; + } + sb.append(post); + return sb.toString(); + } + + public String visitSimpleType(SimpleType type, Void p) { + return "S{" + type.name + "}"; + } + + public String visitArrayType(ArrayType type, Void p) { + return "A{" + print(type.elemType) + "}"; + } + + public String visitMethodType(MethodType type, Void p) { + StringBuilder sb = new StringBuilder(); + sb.append("M{"); + if (type.typeParamTypes != null) + sb.append(print("<", type.typeParamTypes, ">")); + sb.append(print(type.returnType)); + sb.append(print("(", type.paramTypes, ")")); + if (type.throwsTypes != null) + sb.append(print("", type.throwsTypes, "")); + sb.append("}"); + return sb.toString(); + } + + public String visitClassSigType(ClassSigType type, Void p) { + StringBuilder sb = new StringBuilder(); + sb.append("CS{"); + if (type.typeParamTypes != null) + sb.append(print("<", type.typeParamTypes, ">")); + sb.append(print(type.superclassType)); + if (type.superinterfaceTypes != null) + sb.append(print("i(", type.superinterfaceTypes, ")")); + sb.append("}"); + return sb.toString(); + } + + public String visitClassType(ClassType type, Void p) { + StringBuilder sb = new StringBuilder(); + sb.append("C{"); + if (type.outerType != null) { + sb.append(print(type.outerType)); + sb.append("."); + } + sb.append(type.name); + if (type.typeArgs != null) + sb.append(print("<", type.typeArgs, ">")); + sb.append("}"); + return sb.toString(); + } + + public String visitTypeParamType(TypeParamType type, Void p) { + StringBuilder sb = new StringBuilder(); + sb.append("TA{"); + sb.append(type.name); + if (type.classBound != null) { + sb.append(":c"); + sb.append(print(type.classBound)); + } + if (type.interfaceBounds != null) + sb.append(print(":i", type.interfaceBounds, "")); + sb.append("}"); + return sb.toString(); + } + + public String visitWildcardType(WildcardType type, Void p) { + switch (type.kind) { + case UNBOUNDED: + return "W{?}"; + case EXTENDS: + return "W{e," + print(type.boundType) + "}"; + case SUPER: + return "W{s," + print(type.boundType) + "}"; + default: + throw new AssertionError(); + } + } + + }; +} + + +@interface Desc { + String d(); + String t(); +} + +@interface Sig { + String s(); + String t(); +} + +class Clss { } +interface Intf { } +class GenClss<T> { } + +class Test { + // fields + + @Desc(d="Z", t="S{boolean}") + boolean z; + + @Desc(d="B", t="S{byte}") + byte b; + + @Desc(d="C", t="S{char}") + char c; + + @Desc(d="D", t="S{double}") + double d; + + @Desc(d="F", t="S{float}") + float f; + + @Desc(d="I", t="S{int}") + int i; + + @Desc(d="J", t="S{long}") + long l; + + @Desc(d="S", t="S{short}") + short s; + + @Desc(d="LClss;", t="C{Clss}") + Clss clss; + + @Desc(d="LIntf;", t="C{Intf}") + Intf intf; + + @Desc(d="[I", t="A{S{int}}") + int[] ai; + + @Desc(d="[LClss;", t="A{C{Clss}}") + Clss[] aClss; + + @Desc(d="LGenClss;", t="C{GenClss}") + @Sig(s="LGenClss<LClss;>;", t="C{GenClss<C{Clss}>}") + GenClss<Clss> genClass; + + // methods, return types + + @Desc(d="()V", t="M{S{void}()}") + void mv0() { } + + @Desc(d="()I", t="M{S{int}()}") + int mi0() { return 0; } + + @Desc(d="()LClss;", t="M{C{Clss}()}") + Clss mclss0() { return null; } + + @Desc(d="()[I", t="M{A{S{int}}()}") + int[] mai0() { return null; } + + @Desc(d="()[LClss;", t="M{A{C{Clss}}()}") + Clss[] maClss0() { return null; } + + @Desc(d="()LGenClss;", t="M{C{GenClss}()}") + @Sig(s="()LGenClss<LClss;>;", t="M{C{GenClss<C{Clss}>}()}") + GenClss<Clss> mgenClss0() { return null; } + + @Desc(d="()LGenClss;", t="M{C{GenClss}()}") + @Sig(s="()LGenClss<*>;", t="M{C{GenClss<W{?}>}()}") + GenClss<?> mgenClssW0() { return null; } + + @Desc(d="()LGenClss;", t="M{C{GenClss}()}") + @Sig(s="()LGenClss<+LClss;>;", t="M{C{GenClss<W{e,C{Clss}}>}()}") + GenClss<? extends Clss> mgenClssWExtClss0() { return null; } + + @Desc(d="()LGenClss;", t="M{C{GenClss}()}") + @Sig(s="()LGenClss<-LClss;>;", t="M{C{GenClss<W{s,C{Clss}}>}()}") + GenClss<? super Clss> mgenClssWSupClss0() { return null; } + + @Desc(d="()Ljava/lang/Object;", t="M{C{java/lang/Object}()}") + @Sig(s="<T:Ljava/lang/Object;>()TT;", t="M{<TA{T:cC{java/lang/Object}}>S{T}()}") + <T> T mt0() { return null; } + + @Desc(d="()LGenClss;", t="M{C{GenClss}()}") + @Sig(s="<T:Ljava/lang/Object;>()LGenClss<+TT;>;", + t="M{<TA{T:cC{java/lang/Object}}>C{GenClss<W{e,S{T}}>}()}") + <T> GenClss<? extends T> mgenClssWExtT0() { return null; } + + @Desc(d="()LGenClss;", t="M{C{GenClss}()}") + @Sig(s="<T:Ljava/lang/Object;>()LGenClss<-TT;>;", t="M{<TA{T:cC{java/lang/Object}}>C{GenClss<W{s,S{T}}>}()}") + <T> GenClss<? super T> mgenClssWSupT0() { return null; } + + // methods, arg types + + @Desc(d="(I)V", t="M{S{void}(S{int})}") + void mi1(int arg) { } + + @Desc(d="(LClss;)V", t="M{S{void}(C{Clss})}") + void mclss1(Clss arg) { } + + @Desc(d="([I)V", t="M{S{void}(A{S{int}})}") + void mai1(int[] arg) { } + + @Desc(d="([LClss;)V", t="M{S{void}(A{C{Clss}})}") + void maClss1(Clss[] arg) { } + + @Desc(d="(LGenClss;)V", t="M{S{void}(C{GenClss})}") + @Sig(s="(LGenClss<LClss;>;)V", t="M{S{void}(C{GenClss<C{Clss}>})}") + void mgenClss1(GenClss<Clss> arg) { } + + @Desc(d="(LGenClss;)V", t="M{S{void}(C{GenClss})}") + @Sig(s="(LGenClss<*>;)V", t="M{S{void}(C{GenClss<W{?}>})}") + void mgenClssW1(GenClss<?> arg) { } + + @Desc(d="(LGenClss;)V", t="M{S{void}(C{GenClss})}") + @Sig(s="(LGenClss<+LClss;>;)V", t="M{S{void}(C{GenClss<W{e,C{Clss}}>})}") + void mgenClssWExtClss1(GenClss<? extends Clss> arg) { } + + @Desc(d="(LGenClss;)V", t="M{S{void}(C{GenClss})}") + @Sig(s="(LGenClss<-LClss;>;)V", t="M{S{void}(C{GenClss<W{s,C{Clss}}>})}") + void mgenClssWSupClss1(GenClss<? super Clss> arg) { } + + @Desc(d="(Ljava/lang/Object;)V", t="M{S{void}(C{java/lang/Object})}") + @Sig(s="<T:Ljava/lang/Object;>(TT;)V", + t="M{<TA{T:cC{java/lang/Object}}>S{void}(S{T})}") + <T> void mt1(T arg) { } + + @Desc(d="(LGenClss;)V", t="M{S{void}(C{GenClss})}") + @Sig(s="<T:Ljava/lang/Object;>(LGenClss<+TT;>;)V", + t="M{<TA{T:cC{java/lang/Object}}>S{void}(C{GenClss<W{e,S{T}}>})}") + <T> void mgenClssWExtT1(GenClss<? extends T> arg) { } + + @Desc(d="(LGenClss;)V", t="M{S{void}(C{GenClss})}") + @Sig(s="<T:Ljava/lang/Object;>(LGenClss<-TT;>;)V", + t="M{<TA{T:cC{java/lang/Object}}>S{void}(C{GenClss<W{s,S{T}}>})}") + <T> void mgenClssWSupT1(GenClss<? super T> arg) { } + + // methods, throws + + @Desc(d="()V", t="M{S{void}()}") + void m_E() throws Exception { } + + @Desc(d="()V", t="M{S{void}()}") + @Sig(s="<T:Ljava/lang/Throwable;>()V^TT;", + t="M{<TA{T:cC{java/lang/Throwable}}>S{void}()S{T}}") + <T extends Throwable> void m_T() throws T { } + + // inner classes + + static class X { + // no sig + class P { } + + @Sig(s="<TQ:Ljava/lang/Object;>LTest$X$P;", + t="CS{<TA{TQ:cC{java/lang/Object}}>C{Test$X$P}}") + class Q<TQ> extends P { } + + @Sig(s="<TR:Ljava/lang/Object;>LTest$X$Q<TTR;>;", + t="CS{<TA{TR:cC{java/lang/Object}}>C{Test$X$Q<S{TR}>}}") + class R<TR> extends Q<TR> { } + } + + @Sig(s="<TY:Ljava/lang/Object;>Ljava/lang/Object;", + t="CS{<TA{TY:cC{java/lang/Object}}>C{java/lang/Object}}") + static class Y<TY> { + // no sig + class P { } + + @Sig(s="<TQ:Ljava/lang/Object;>LTest$Y<TTY;>.P;", + t="CS{<TA{TQ:cC{java/lang/Object}}>C{C{Test$Y<S{TY}>}.P}}") + class Q<TQ> extends P { } + + @Sig(s="<TR:Ljava/lang/Object;>LTest$Y<TTY;>.Q<TTR;>;", + t="CS{<TA{TR:cC{java/lang/Object}}>C{C{Test$Y<S{TY}>}.Q<S{TR}>}}") + class R<TR> extends Q<TR> { + // no sig + class R1 { } + + @Sig(s="<TR2:Ljava/lang/Object;>LTest$Y<TTY;>.R<TTR;>.R1;", + t="CS{<TA{TR2:cC{java/lang/Object}}>C{C{C{Test$Y<S{TY}>}.R<S{TR}>}.R1}}") + class R2<TR2> extends R1 { } + } + + @Sig(s="LTest$Y<TTY;>.Q<TTY;>;", t="C{C{Test$Y<S{TY}>}.Q<S{TY}>}") + class S extends Q<TY> { + // no sig + class S1 { } + + @Sig(s="<TS2:Ljava/lang/Object;>LTest$Y<TTY;>.S.S1;", + t="CS{<TA{TS2:cC{java/lang/Object}}>C{C{C{Test$Y<S{TY}>}.S}.S1}}") + class S2<TS2> extends S1 { } + + @Sig(s="LTest$Y<TTY;>.S.S2<TTY;>;", + t="C{C{C{Test$Y<S{TY}>}.S}.S2<S{TY}>}") + class S3 extends S2<TY> { } + } + } +} + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/langtools/test/tools/javap/classfile/T6887895.java Wed Oct 28 10:37:17 2009 -0700 @@ -0,0 +1,121 @@ +/* + * 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 6887895 + * @summary CONSTANT_Class_info getBaseName does not handle arrays of primitives correctly + */ + +import java.io.*; +import java.net.*; +import java.util.*; +import com.sun.tools.classfile.*; +import com.sun.tools.classfile.ConstantPool.*; + +public class T6887895 { + public static void main(String[] args) throws Exception { + new T6887895().run(); + } + + void run() throws Exception { + Set<String> found = new TreeSet<String>(); + + ClassFile cf = getClassFile("T6887895$Test.class"); + for (CPInfo cpInfo: cf.constant_pool.entries()) { + if (cpInfo instanceof CONSTANT_Class_info) { + CONSTANT_Class_info info = (CONSTANT_Class_info) cpInfo; + String name = info.getName(); + String baseName = info.getBaseName(); + System.out.println("found: " + name + " " + baseName); + if (baseName != null) + found.add(baseName); + } + } + + String[] expectNames = { + "java/lang/Object", + "java/lang/String", + "T6887895", + "T6887895$Test" + }; + + Set<String> expect = new TreeSet<String>(Arrays.asList(expectNames)); + if (!found.equals(expect)) { + System.err.println("found: " + found); + System.err.println("expect: " + expect); + throw new Exception("unexpected values found"); + } + } + + ClassFile getClassFile(String name) throws IOException, ConstantPoolException { + URL url = getClass().getResource(name); + InputStream in = url.openStream(); + try { + return ClassFile.read(in); + } finally { + in.close(); + } + } + + class Test { + void m() { + boolean[] az = new boolean[0]; + boolean[][] aaz = new boolean[0][]; + boolean[][][] aaaz = new boolean[0][][]; + + byte[] ab = new byte[0]; + byte[][] aab = new byte[0][]; + byte[][][] aaab = new byte[0][][]; + + char[] ac = new char[0]; + char[][] aac = new char[0][]; + char[][][] aaac = new char[0][][]; + + double[] ad = new double[0]; + double[][] aad = new double[0][]; + double[][][] aaad = new double[0][][]; + + float[] af = new float[0]; + float[][] aaf = new float[0][]; + float[][][] aaaf = new float[0][][]; + + int[] ai = new int[0]; + int[][] aai = new int[0][]; + int[][][] aaai = new int[0][][]; + + long[] al = new long[0]; + long[][] aal = new long[0][]; + long[][][] aaal = new long[0][][]; + + short[] as = new short[0]; + short[][] aas = new short[0][]; + short[][][] aaas = new short[0][][]; + + String[] aS = new String[0]; + String[][] aaS = new String[0][]; + String[][][] aaaS = new String[0][][]; + } + } +} +