001/* 002 * Copyright 2008-2018 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright (C) 2008-2018 Ping Identity Corporation 007 * 008 * This program is free software; you can redistribute it and/or modify 009 * it under the terms of the GNU General Public License (GPLv2 only) 010 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only) 011 * as published by the Free Software Foundation. 012 * 013 * This program is distributed in the hope that it will be useful, 014 * but WITHOUT ANY WARRANTY; without even the implied warranty of 015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 016 * GNU General Public License for more details. 017 * 018 * You should have received a copy of the GNU General Public License 019 * along with this program; if not, see <http://www.gnu.org/licenses>. 020 */ 021package com.unboundid.ldap.sdk; 022 023 024 025import com.unboundid.util.Extensible; 026import com.unboundid.util.ThreadSafety; 027import com.unboundid.util.ThreadSafetyLevel; 028 029import static com.unboundid.util.Debug.*; 030 031 032 033/** 034 * This class defines an API that can be used to select between multiple 035 * directory servers when establishing a connection. Implementations are free 036 * to use any kind of logic that they desire when selecting the server to which 037 * the connection is to be established. They may also support the use of 038 * health checks to determine whether the created connections are suitable for 039 * use. 040 * <BR><BR> 041 * Implementations MUST be threadsafe to allow for multiple concurrent attempts 042 * to establish new connections. 043 */ 044@Extensible() 045@ThreadSafety(level=ThreadSafetyLevel.INTERFACE_THREADSAFE) 046public abstract class ServerSet 047{ 048 /** 049 * Creates a new instance of this server set. 050 */ 051 protected ServerSet() 052 { 053 // No implementation is required. 054 } 055 056 057 058 /** 059 * Indicates whether connections created by this server set will be 060 * authenticated. 061 * 062 * @return {@code true} if connections created by this server set will be 063 * authenticated, or {@code false} if not. 064 */ 065 public boolean includesAuthentication() 066 { 067 return false; 068 } 069 070 071 072 /** 073 * Indicates whether connections created by this server set will have 074 * post-connect processing performed. 075 * 076 * @return {@code true} if connections created by this server set will have 077 * post-connect processing performed, or {@code false} if not. 078 */ 079 public boolean includesPostConnectProcessing() 080 { 081 return false; 082 } 083 084 085 086 /** 087 * Attempts to establish a connection to one of the directory servers in this 088 * server set. The connection that is returned must be established. The 089 * {@link #includesAuthentication()} must return true if and only if the 090 * connection will also be authenticated, and the 091 * {@link #includesPostConnectProcessing()} method must return true if and 092 * only if pre-authentication and post-authentication post-connect processing 093 * will have been performed. The caller may determine the server to which the 094 * connection is established using the 095 * {@link LDAPConnection#getConnectedAddress} and 096 * {@link LDAPConnection#getConnectedPort} methods. 097 * 098 * @return An {@code LDAPConnection} object that is established to one of the 099 * servers in this server set. 100 * 101 * @throws LDAPException If it is not possible to establish a connection to 102 * any of the servers in this server set. 103 */ 104 public abstract LDAPConnection getConnection() 105 throws LDAPException; 106 107 108 109 /** 110 * Attempts to establish a connection to one of the directory servers in this 111 * server set, using the provided health check to further validate the 112 * connection. The connection that is returned must be established. The 113 * {@link #includesAuthentication()} must return true if and only if the 114 * connection will also be authenticated, and the 115 * {@link #includesPostConnectProcessing()} method must return true if and 116 * only if pre-authentication and post-authentication post-connect processing 117 * will have been performed. The caller may determine the server to which the 118 * connection is established using the 119 * {@link LDAPConnection#getConnectedAddress} and 120 * {@link LDAPConnection#getConnectedPort} methods. 121 * 122 * @param healthCheck The health check to use to verify the health of the 123 * newly-created connection. It may be {@code null} if 124 * no additional health check should be performed. If it 125 * is non-{@code null} and this server set performs 126 * authentication, then the health check's 127 * {@code ensureConnectionValidAfterAuthentication} 128 * method will be invoked immediately after the bind 129 * operation is processed (regardless of whether the bind 130 * was successful or not). And regardless of whether 131 * this server set performs authentication, the 132 * health check's {@code ensureNewConnectionValid} 133 * method must be invoked on the connection to ensure 134 * that it is valid immediately before it is returned. 135 * 136 * @return An {@code LDAPConnection} object that is established to one of the 137 * servers in this server set. 138 * 139 * @throws LDAPException If it is not possible to establish a connection to 140 * any of the servers in this server set. 141 */ 142 public LDAPConnection getConnection( 143 final LDAPConnectionPoolHealthCheck healthCheck) 144 throws LDAPException 145 { 146 final LDAPConnection c = getConnection(); 147 148 if (healthCheck != null) 149 { 150 try 151 { 152 healthCheck.ensureNewConnectionValid(c); 153 } 154 catch (final LDAPException le) 155 { 156 debugException(le); 157 c.close(); 158 throw le; 159 } 160 } 161 162 return c; 163 } 164 165 166 167 /** 168 * Performs the appropriate bind, post-connect, and health check processing 169 * for the provided connection, in the provided order. The processing 170 * performed will include: 171 * <OL> 172 * <LI> 173 * If the provided {@code postConnectProcessor} is not {@code null}, then 174 * invoke its {@code processPreAuthenticatedConnection} method on the 175 * provided connection. If this method throws an {@code LDAPException}, 176 * then it will propagated up to the caller of this method. 177 * </LI> 178 * <LI> 179 * If the provided {@code bindRequest} is not {@code null}, then 180 * authenticate the connection using that request. If the provided 181 * {@code healthCheck} is also not {@code null}, then invoke its 182 * {@code ensureConnectionValidAfterAuthentication} method on the 183 * connection, even if the bind was not successful. If the health check 184 * throws an {@code LDAPException}, then it will be propagated up to the 185 * caller of this method. If there is no health check or if it did not 186 * throw an exception but the bind attempt did throw an exception, then 187 * propagate that exception instead. 188 * </LI> 189 * <LI> 190 * If the provided {@code postConnectProcessor} is not {@code null}, then 191 * invoke its {@code processPostAuthenticatedConnection} method on the 192 * provided connection. If this method throws an {@code LDAPException}, 193 * then it will propagated up to the caller of this method. 194 * </LI> 195 * <LI> 196 * If the provided {@code healthCheck} is not {@code null}, then invoke 197 * its {@code ensureNewConnectionValid} method on the provided connection. 198 * If this method throws an {@code LDAPException}, then it will be 199 * propagated up to the caller of this method. 200 * </LI> 201 * </OL> 202 * 203 * @param connection The connection to be processed. It must not 204 * be {@code null}, and it must be established. 205 * Note that if an {@code LDAPException} is 206 * thrown by this method or anything that it 207 * calls, then the connection will have been 208 * closed before that exception has been 209 * propagated up to the caller of this method. 210 * @param bindRequest The bind request to use to authenticate the 211 * connection. It may be {@code null} if no 212 * authentication should be performed. 213 * @param postConnectProcessor The post-connect processor to invoke on the 214 * provided connection. It may be {@code null} 215 * if no post-connect processing should be 216 * performed. 217 * @param healthCheck The health check to use to verify the health 218 * of connection. It may be {@code null} if no 219 * health check processing should be performed. 220 * 221 * @throws LDAPException If a problem is encountered during any of the 222 * processing performed by this method. If an 223 * exception is thrown, then the provided connection 224 * will have been closed. 225 */ 226 protected static void doBindPostConnectAndHealthCheckProcessing( 227 final LDAPConnection connection, 228 final BindRequest bindRequest, 229 final PostConnectProcessor postConnectProcessor, 230 final LDAPConnectionPoolHealthCheck healthCheck) 231 throws LDAPException 232 { 233 try 234 { 235 if (postConnectProcessor != null) 236 { 237 postConnectProcessor.processPreAuthenticatedConnection(connection); 238 } 239 240 if (bindRequest != null) 241 { 242 BindResult bindResult; 243 LDAPException bindException = null; 244 try 245 { 246 bindResult = connection.bind(bindRequest.duplicate()); 247 } 248 catch (final LDAPException le) 249 { 250 debugException(le); 251 bindException = le; 252 bindResult = new BindResult(le); 253 } 254 255 if (healthCheck != null) 256 { 257 healthCheck.ensureConnectionValidAfterAuthentication(connection, 258 bindResult); 259 } 260 261 if (bindException != null) 262 { 263 throw bindException; 264 } 265 } 266 267 if (postConnectProcessor != null) 268 { 269 postConnectProcessor.processPostAuthenticatedConnection(connection); 270 } 271 272 if (healthCheck != null) 273 { 274 healthCheck.ensureNewConnectionValid(connection); 275 } 276 } 277 catch (final LDAPException le) 278 { 279 debugException(le); 280 connection.closeWithoutUnbind(); 281 throw le; 282 } 283 } 284 285 286 287 /** 288 * Retrieves a string representation of this server set. 289 * 290 * @return A string representation of this server set. 291 */ 292 @Override() 293 public String toString() 294 { 295 final StringBuilder buffer = new StringBuilder(); 296 toString(buffer); 297 return buffer.toString(); 298 } 299 300 301 302 /** 303 * Appends a string representation of this server set to the provided buffer. 304 * 305 * @param buffer The buffer to which the string representation should be 306 * appended. 307 */ 308 public void toString(final StringBuilder buffer) 309 { 310 buffer.append("ServerSet(className="); 311 buffer.append(getClass().getName()); 312 buffer.append(')'); 313 } 314}