001: /*
002: * Copyright 2002-2005 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025:
026: package com.sun.jndi.ldap;
027:
028: import java.util.Arrays; // JDK 1.2
029: import java.io.OutputStream;
030: import javax.naming.ldap.Control;
031: import java.lang.reflect.Method;
032: import javax.net.SocketFactory;
033:
034: /**
035: * Represents identity information about an anonymous LDAP connection.
036: * This base class contains the following information:
037: * - protocol version number
038: * - server's hostname (case-insensitive)
039: * - server's port number
040: * - prototype type (plain or ssl)
041: * - controls to be sent with the LDAP bind request
042: *
043: * All other identity classes must be a subclass of ClientId.
044: * Identity subclasses would add more distinguishing information, depending
045: * on the type of authentication that the connection is to have.
046: *
047: * The equals() and hashCode() methods of this class and its subclasses are
048: * important because they are used to determine whether two requests for
049: * the same connection are identical, and thus whether the same connection
050: * may be shared. This is especially important for authenticated connections
051: * because a mistake would result in a serious security violation.
052: *
053: * @author Rosanna Lee
054: */
055: class ClientId {
056: final private int version;
057: final private String hostname;
058: final private int port;
059: final private String protocol;
060: final private Control[] bindCtls;
061: final private OutputStream trace;
062: final private String socketFactory;
063: final private int myHash;
064: final private int ctlHash;
065:
066: private SocketFactory factory = null;
067: private Method sockComparator = null;
068: private boolean isDefaultSockFactory = false;
069: final public static boolean debug = false;
070:
071: ClientId(int version, String hostname, int port, String protocol,
072: Control[] bindCtls, OutputStream trace, String socketFactory) {
073: this .version = version;
074: this .hostname = hostname.toLowerCase(); // ignore case
075: this .port = port;
076: this .protocol = protocol;
077: this .bindCtls = (bindCtls != null ? (Control[]) bindCtls
078: .clone() : null);
079: this .trace = trace;
080: //
081: // Needed for custom socket factory pooling
082: //
083: this .socketFactory = socketFactory;
084: if ((socketFactory != null)
085: && !socketFactory.equals(LdapCtx.DEFAULT_SSL_FACTORY)) {
086: try {
087: Class socketFactoryClass = Obj.helper
088: .loadClass(socketFactory);
089: Class objClass = Class.forName("java.lang.Object");
090: this .sockComparator = socketFactoryClass.getMethod(
091: "compare", new Class[] { objClass, objClass });
092: Method getDefault = socketFactoryClass.getMethod(
093: "getDefault", new Class[] {});
094: this .factory = (SocketFactory) getDefault.invoke(null,
095: new Object[] {});
096: } catch (Exception e) {
097: // Ignore it here, the same exceptions are/will be handled by
098: // LdapPoolManager and Connection classes.
099: if (debug) {
100: System.out
101: .println("ClientId received an exception");
102: e.printStackTrace();
103: }
104: }
105: } else {
106: isDefaultSockFactory = true;
107: }
108:
109: // The SocketFactory field is not used in the myHash
110: // computation as there is no right way to compute the hash code
111: // for this field. There is no harm in skipping it from the hash
112: // computation
113: myHash = version
114: + port
115: + (trace != null ? trace.hashCode() : 0)
116: + (this .hostname != null ? this .hostname.hashCode() : 0)
117: + (protocol != null ? protocol.hashCode() : 0)
118: + (ctlHash = hashCodeControls(bindCtls));
119: }
120:
121: public boolean equals(Object obj) {
122: if (!(obj instanceof ClientId)) {
123: return false;
124: }
125:
126: ClientId other = (ClientId) obj;
127:
128: return myHash == other.myHash
129: && version == other.version
130: && port == other.port
131: && trace == other.trace
132: && (hostname == other.hostname // null OK
133: || (hostname != null && hostname.equals(other.hostname)))
134: && (protocol == other.protocol // null OK
135: || (protocol != null && protocol.equals(other.protocol)))
136: && ctlHash == other.ctlHash
137: && (equalsControls(bindCtls, other.bindCtls))
138: && (equalsSockFactory(other));
139: }
140:
141: public int hashCode() {
142: return myHash;
143: }
144:
145: private static int hashCodeControls(Control[] c) {
146: if (c == null) {
147: return 0;
148: }
149:
150: int code = 0;
151: for (int i = 0; i < c.length; i++) {
152: code = code * 31 + c[i].getID().hashCode();
153: }
154: return code;
155: }
156:
157: private static boolean equalsControls(Control[] a, Control[] b) {
158: if (a == b) {
159: return true; // both null or same
160: }
161: if (a == null || b == null) {
162: return false; // one is non-null
163: }
164: if (a.length != b.length) {
165: return false;
166: }
167:
168: for (int i = 0; i < a.length; i++) {
169: if (!a[i].getID().equals(b[i].getID())
170: || a[i].isCritical() != b[i].isCritical()
171: || !Arrays.equals(a[i].getEncodedValue(), b[i]
172: .getEncodedValue())) {
173: return false;
174: }
175: }
176: return true;
177: }
178:
179: private boolean equalsSockFactory(ClientId other) {
180: if (this .isDefaultSockFactory && other.isDefaultSockFactory) {
181: return true;
182: } else if (!other.isDefaultSockFactory) {
183: return invokeComparator(other, this );
184: } else {
185: return invokeComparator(this , other);
186: }
187: }
188:
189: // delegate the comparison work to the SocketFactory class
190: // as there is no enough information here, to do the comparison
191: private boolean invokeComparator(ClientId c1, ClientId c2) {
192: Object ret;
193: try {
194: ret = (c1.sockComparator).invoke(c1.factory,
195: c1.socketFactory, c2.socketFactory);
196: } catch (Exception e) {
197: if (debug) {
198: System.out.println("ClientId received an exception");
199: e.printStackTrace();
200: }
201: // Failed to invoke the comparator; flag unequality
202: return false;
203: }
204: if (((Integer) ret) == 0) {
205: return true;
206: }
207: return false;
208: }
209:
210: private static String toStringControls(Control[] ctls) {
211: if (ctls == null) {
212: return "";
213: }
214: StringBuffer str = new StringBuffer();
215: for (int i = 0; i < ctls.length; i++) {
216: str.append(ctls[i].getID());
217: str.append(' ');
218: }
219: return str.toString();
220: }
221:
222: public String toString() {
223: return (hostname + ":" + port + ":"
224: + (protocol != null ? protocol : "") + ":"
225: + toStringControls(bindCtls) + ":" + socketFactory);
226: }
227: }
|