blob: 6253111315de254980577c74f035ecb0d9911cae [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2002-2005 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26package com.sun.jndi.ldap;
27
28import java.util.Arrays; // JDK 1.2
29import java.io.OutputStream;
30import javax.naming.ldap.Control;
31import java.lang.reflect.Method;
32import javax.net.SocketFactory;
33
34/**
35 * Represents identity information about an anonymous LDAP connection.
36 * This base class contains the following information:
37 * - protocol version number
38 * - server's hostname (case-insensitive)
39 * - server's port number
40 * - prototype type (plain or ssl)
41 * - controls to be sent with the LDAP bind request
42 *
43 * All other identity classes must be a subclass of ClientId.
44 * Identity subclasses would add more distinguishing information, depending
45 * on the type of authentication that the connection is to have.
46 *
47 * The equals() and hashCode() methods of this class and its subclasses are
48 * important because they are used to determine whether two requests for
49 * the same connection are identical, and thus whether the same connection
50 * may be shared. This is especially important for authenticated connections
51 * because a mistake would result in a serious security violation.
52 *
53 * @author Rosanna Lee
54 */
55class ClientId {
56 final private int version;
57 final private String hostname;
58 final private int port;
59 final private String protocol;
60 final private Control[] bindCtls;
61 final private OutputStream trace;
62 final private String socketFactory;
63 final private int myHash;
64 final private int ctlHash;
65
66 private SocketFactory factory = null;
67 private Method sockComparator = null;
68 private boolean isDefaultSockFactory = false;
69 final public static boolean debug = false;
70
71 ClientId(int version, String hostname, int port, String protocol,
72 Control[] bindCtls, OutputStream trace, String socketFactory) {
73 this.version = version;
74 this.hostname = hostname.toLowerCase(); // ignore case
75 this.port = port;
76 this.protocol = protocol;
77 this.bindCtls = (bindCtls != null ? (Control[]) bindCtls.clone() : null);
78 this.trace = trace;
79 //
80 // Needed for custom socket factory pooling
81 //
82 this.socketFactory = socketFactory;
83 if ((socketFactory != null) &&
84 !socketFactory.equals(LdapCtx.DEFAULT_SSL_FACTORY)) {
85 try {
86 Class socketFactoryClass = Obj.helper.loadClass(socketFactory);
87 Class objClass = Class.forName("java.lang.Object");
88 this.sockComparator = socketFactoryClass.getMethod(
89 "compare", new Class[]{objClass, objClass});
90 Method getDefault =
91 socketFactoryClass.getMethod("getDefault", new Class[]{});
92 this.factory = (SocketFactory) getDefault.invoke(null, new Object[]{});
93 } catch (Exception e) {
94 // Ignore it here, the same exceptions are/will be handled by
95 // LdapPoolManager and Connection classes.
96 if (debug) {
97 System.out.println("ClientId received an exception");
98 e.printStackTrace();
99 }
100 }
101 } else {
102 isDefaultSockFactory = true;
103 }
104
105 // The SocketFactory field is not used in the myHash
106 // computation as there is no right way to compute the hash code
107 // for this field. There is no harm in skipping it from the hash
108 // computation
109 myHash = version + port
110 + (trace != null ? trace.hashCode() : 0)
111 + (this.hostname != null ? this.hostname.hashCode() : 0)
112 + (protocol != null ? protocol.hashCode() : 0)
113 + (ctlHash=hashCodeControls(bindCtls));
114 }
115
116 public boolean equals(Object obj) {
117 if (!(obj instanceof ClientId)) {
118 return false;
119 }
120
121 ClientId other = (ClientId)obj;
122
123 return myHash == other.myHash
124 && version == other.version
125 && port == other.port
126 && trace == other.trace
127 && (hostname == other.hostname // null OK
128 || (hostname != null && hostname.equals(other.hostname)))
129 && (protocol == other.protocol // null OK
130 || (protocol != null && protocol.equals(other.protocol)))
131 && ctlHash == other.ctlHash
132 && (equalsControls(bindCtls, other.bindCtls))
133 && (equalsSockFactory(other));
134 }
135
136 public int hashCode() {
137 return myHash;
138 }
139
140 private static int hashCodeControls(Control[] c) {
141 if (c == null) {
142 return 0;
143 }
144
145 int code = 0;
146 for (int i = 0; i < c.length; i++) {
147 code = code * 31 + c[i].getID().hashCode();
148 }
149 return code;
150 }
151
152 private static boolean equalsControls(Control[] a, Control[] b) {
153 if (a == b) {
154 return true; // both null or same
155 }
156 if (a == null || b == null) {
157 return false; // one is non-null
158 }
159 if (a.length != b.length) {
160 return false;
161 }
162
163 for (int i = 0; i < a.length; i++) {
164 if (!a[i].getID().equals(b[i].getID())
165 || a[i].isCritical() != b[i].isCritical()
166 || !Arrays.equals(a[i].getEncodedValue(),
167 b[i].getEncodedValue())) {
168 return false;
169 }
170 }
171 return true;
172 }
173
174 private boolean equalsSockFactory(ClientId other) {
175 if (this.isDefaultSockFactory && other.isDefaultSockFactory) {
176 return true;
177 }
178 else if (!other.isDefaultSockFactory) {
179 return invokeComparator(other, this);
180 } else {
181 return invokeComparator(this, other);
182 }
183 }
184
185 // delegate the comparison work to the SocketFactory class
186 // as there is no enough information here, to do the comparison
187 private boolean invokeComparator(ClientId c1, ClientId c2) {
188 Object ret;
189 try {
190 ret = (c1.sockComparator).invoke(
191 c1.factory, c1.socketFactory, c2.socketFactory);
192 } catch(Exception e) {
193 if (debug) {
194 System.out.println("ClientId received an exception");
195 e.printStackTrace();
196 }
197 // Failed to invoke the comparator; flag unequality
198 return false;
199 }
200 if (((Integer) ret) == 0) {
201 return true;
202 }
203 return false;
204 }
205
206 private static String toStringControls(Control[] ctls) {
207 if (ctls == null) {
208 return "";
209 }
210 StringBuffer str = new StringBuffer();
211 for (int i = 0; i < ctls.length; i++) {
212 str.append(ctls[i].getID());
213 str.append(' ');
214 }
215 return str.toString();
216 }
217
218 public String toString() {
219 return (hostname + ":" + port + ":" +
220 (protocol != null ? protocol : "") + ":" +
221 toStringControls(bindCtls) + ":" +
222 socketFactory);
223 }
224}