/* * Copyright (c) 2002, 2005, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.sun.jndi.ldap; import java.util.Arrays; // JDK 1.2 import java.io.OutputStream; import javax.naming.ldap.Control; import java.lang.reflect.Method; import javax.net.SocketFactory; /** * Represents identity information about an anonymous LDAP connection. * This base class contains the following information: * - protocol version number * - server's hostname (case-insensitive) * - server's port number * - prototype type (plain or ssl) * - controls to be sent with the LDAP bind request * * All other identity classes must be a subclass of ClientId. * Identity subclasses would add more distinguishing information, depending * on the type of authentication that the connection is to have. * * The equals() and hashCode() methods of this class and its subclasses are * important because they are used to determine whether two requests for * the same connection are identical, and thus whether the same connection * may be shared. This is especially important for authenticated connections * because a mistake would result in a serious security violation. * * @author Rosanna Lee */ class ClientId { final private int version; final private String hostname; final private int port; final private String protocol; final private Control[] bindCtls; final private OutputStream trace; final private String socketFactory; final private int myHash; final private int ctlHash; private SocketFactory factory = null; private Method sockComparator = null; private boolean isDefaultSockFactory = false; final public static boolean debug = false; ClientId(int version, String hostname, int port, String protocol, Control[] bindCtls, OutputStream trace, String socketFactory) { this.version = version; this.hostname = hostname.toLowerCase(); // ignore case this.port = port; this.protocol = protocol; this.bindCtls = (bindCtls != null ? (Control[]) bindCtls.clone() : null); this.trace = trace; // // Needed for custom socket factory pooling // this.socketFactory = socketFactory; if ((socketFactory != null) && !socketFactory.equals(LdapCtx.DEFAULT_SSL_FACTORY)) { try { Class socketFactoryClass = Obj.helper.loadClass(socketFactory); Class objClass = Class.forName("java.lang.Object"); this.sockComparator = socketFactoryClass.getMethod( "compare", new Class[]{objClass, objClass}); Method getDefault = socketFactoryClass.getMethod("getDefault", new Class[]{}); this.factory = (SocketFactory) getDefault.invoke(null, new Object[]{}); } catch (Exception e) { // Ignore it here, the same exceptions are/will be handled by // LdapPoolManager and Connection classes. if (debug) { System.out.println("ClientId received an exception"); e.printStackTrace(); } } } else { isDefaultSockFactory = true; } // The SocketFactory field is not used in the myHash // computation as there is no right way to compute the hash code // for this field. There is no harm in skipping it from the hash // computation myHash = version + port + (trace != null ? trace.hashCode() : 0) + (this.hostname != null ? this.hostname.hashCode() : 0) + (protocol != null ? protocol.hashCode() : 0) + (ctlHash=hashCodeControls(bindCtls)); } public boolean equals(Object obj) { if (!(obj instanceof ClientId)) { return false; } ClientId other = (ClientId)obj; return myHash == other.myHash && version == other.version && port == other.port && trace == other.trace && (hostname == other.hostname // null OK || (hostname != null && hostname.equals(other.hostname))) && (protocol == other.protocol // null OK || (protocol != null && protocol.equals(other.protocol))) && ctlHash == other.ctlHash && (equalsControls(bindCtls, other.bindCtls)) && (equalsSockFactory(other)); } public int hashCode() { return myHash; } private static int hashCodeControls(Control[] c) { if (c == null) { return 0; } int code = 0; for (int i = 0; i < c.length; i++) { code = code * 31 + c[i].getID().hashCode(); } return code; } private static boolean equalsControls(Control[] a, Control[] b) { if (a == b) { return true; // both null or same } if (a == null || b == null) { return false; // one is non-null } if (a.length != b.length) { return false; } for (int i = 0; i < a.length; i++) { if (!a[i].getID().equals(b[i].getID()) || a[i].isCritical() != b[i].isCritical() || !Arrays.equals(a[i].getEncodedValue(), b[i].getEncodedValue())) { return false; } } return true; } private boolean equalsSockFactory(ClientId other) { if (this.isDefaultSockFactory && other.isDefaultSockFactory) { return true; } else if (!other.isDefaultSockFactory) { return invokeComparator(other, this); } else { return invokeComparator(this, other); } } // delegate the comparison work to the SocketFactory class // as there is no enough information here, to do the comparison private boolean invokeComparator(ClientId c1, ClientId c2) { Object ret; try { ret = (c1.sockComparator).invoke( c1.factory, c1.socketFactory, c2.socketFactory); } catch(Exception e) { if (debug) { System.out.println("ClientId received an exception"); e.printStackTrace(); } // Failed to invoke the comparator; flag unequality return false; } if (((Integer) ret) == 0) { return true; } return false; } private static String toStringControls(Control[] ctls) { if (ctls == null) { return ""; } StringBuffer str = new StringBuffer(); for (int i = 0; i < ctls.length; i++) { str.append(ctls[i].getID()); str.append(' '); } return str.toString(); } public String toString() { return (hostname + ":" + port + ":" + (protocol != null ? protocol : "") + ":" + toStringControls(bindCtls) + ":" + socketFactory); } }